当前位置: 首页 > 知识库问答 >
问题:

Android 4.4(KitKat)上的Android Gallery为Intent返回不同的URI。获取内容的操作

鲜于岳
2023-03-14

KitKat之前(或新库之前)的Intent。ACTION_GET_CONTENT返回如下的URI

content://media/external/images/media/3951.

使用ContentResolver并查询MediaStore。图像。媒体数据返回了文件URL。

然而,在KitKat中,库返回一个URI(通过“Last”),如下所示:

content://com.android.providers.media.documents/document/image:3951

我该怎么处理?

共有3个答案

贲凌
2023-03-14

有同样的问题,尝试了上面的解决方案,但是尽管它通常有效,出于某种原因,我在Uri内容提供程序上拒绝了一些图像的权限,尽管我正确添加了android.permission.MANAGE_DOCUMENTS权限。

无论如何,找到了另一种解决方案,即强制打开图像库而不是KITKAT文档视图,方法是:

// KITKAT

i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, CHOOSE_IMAGE_REQUEST);

然后加载图像:

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
                BitmapFactory.decodeStream(input , null, opts);

编辑

操作\u打开\u文档可能要求您保留权限标志等,通常会导致安全异常。。。

其他解决方案是使用ACTION_GET_CONTENTc.getContentResolver(). openInputStream(selectedImageURI)相结合,这将在预KK和KK上工作。Kitkat将使用新的文档视图,然后这个解决方案将适用于所有应用程序,如照片,画廊,文件资源管理器,Dropbox,谷歌驱动器等...),但请记住,当使用此解决方案时,您必须在您的onActivityResult()中创建图像并将其存储例如在SD卡上。在下次应用程序启动时从保存的uri中重新生成此图像会在内容解析器上引发安全异常,即使您添加了Google API文档中描述的权限标志(这就是我做一些测试时发生的情况)

此外,Android开发者API指南建议:

操作\u打开\u文档不打算取代操作\u获取\u内容。您应该使用的版本取决于应用程序的需要:

如果您希望应用程序只需读取/导入数据,请使用操作获取内容。通过这种方法,应用程序导入数据的副本,例如图像文件

如果您希望应用程序能够长期、持久地访问文档提供商拥有的文档,请使用ACTION_OPEN_DOCUMENT。例如,照片编辑应用程序允许用户编辑存储在文档提供商中的图像。

慎芷阳
2023-03-14

试试这个:

if (Build.VERSION.SDK_INT <19){
    Intent intent = new Intent(); 
    intent.setType("image/jpeg");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/jpeg");
    startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) return;
    if (null == data) return;
    Uri originalUri = null;
    if (requestCode == GALLERY_INTENT_CALLED) {
        originalUri = data.getData();
    } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
        originalUri = data.getData();
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.
        getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
    }

    loadSomeStreamAsynkTask(originalUri);

}

大概需要

@SuppressLint(“新API”)

对于

TakePersistableUri权限

拓拔阎宝
2023-03-14

这不需要特殊权限,可与存储访问框架以及非正式的ContentProvider模式(数据字段中的文件路径)一起使用。

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @param selection (Optional) Filter used in the query.
 * @param selectionArgs (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri, String selection,
        String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}


/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

请在此处查看此方法的最新版本。

 类似资料:
  • 我正在尝试使用Intent打开文件。操作\u获取\u类似于此帖子的内容: Android打开一个文件与ACTION_GET_CONTENT结果到不同的Uri的 但是这里只是一个解决方案,如何使用不同的SDK/文件夹获取文件名,而不是不同的设备。获得Uri的意图也保持不变。 我想开门。png文件。 Uri.getPath()为两个设备(都. png文件存储在下载文件夹): 因此,问题是,如果我使用

  • 问题内容: 我将以一个非常基本的hello world终结点为例 如果我有此端点,并且希望能够转到/ hello并检索视图。 如果我传入诸如content-type之类的特定请求参数,是否可以使用SAME 路径将模型检索为json? 问题答案: 我不确定我理解你的意思。 如果您希望能够向发出请求并获得具有不同内容类型的两个不同响应,那么可以。 将方法标识为请求处理程序,但它也提供了一些选项来限制应

  • 我将使用一个非常基本的hello worldendpoint作为示例 如果我有这个endpoint,并且我希望能够 /hello并检索视图。 如果传入特定的请求参数(如内容类型),是否可以使用相同的路径以json的形式检索模型?

  • 我有一个应用程序具有这种转换: 输入后,我必须检查一个标志。然后我必须将其作为意图(让我们说)传递给。在中做了一些事情后,它会在按下按钮后返回。我所做的只是再次传递值为true的,然后再次启动活动C。所以发生的是它创建了另一个活动C。 我想做的是,我不必启动一个新的活动C,只需调用super即可使用以前的C。onBackPressed()。但我无法传递intentX的新值。有没有其他方法来实现我想

  • 问题内容: 我的Java应用程序需要具有比较文件系统中两个不同文件并确定它们的二进制内容是否相同的能力。 这是我当前的代码: 任何有关如何正确进行比较功能的提示或建议,将不胜感激。 问题答案: 最简单的方法是将内容读取为两个字符串,例如 ,然后对这些执行。您是否需要更复杂的差分功能?

  • 问题内容: 我正在redis中进行setbit操作,以标记特定日期哪些用户在线。 我正在做一个redis get操作来获取密钥的值。 输出是 我的问题是在“ h”键处设置了第0位1。它应该返回128但返回65533。为什么这样? 我的最终目标是从redis中以二进制形式获取位图,以便我可以确定在特定日期哪些用户处于活动状态。 问题答案: 发生此错误是由于utf-8编码。当我们将第0位设置为1时,它