图库选择器在Android 10上使用文件方案返回Uri,如何读取?

问题描述

我的应用程序具有targetSdkVersion = 29(新的作用域存储策略),并且未在清单中启用requestlegacyexternalstorage标志(我不想启用它)。

我试图撰写意图的方式:

Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"),PICK_IMAGE);

也尝试过

val galleryIntent = Intent(Intent.ACTION_PICK).apply {
    type = PICK_INTENT_TYPE
}
flowInterruptBlocker?.blockFlowInterruption()
startActivityForResult(galleryIntent,galLERY_INTENT_REQUEST_CODE)

还尝试向意图添加CATEGORY_OPENABLE。这些都不起作用。

因此,当我发送意图从画廊中选择图像时,一些来自游戏市场的应用程序会向我返回一个带有文件方案的Uri,例如:file:///storage/emulated/0/Download/sample.jpg >

当我尝试将其转换为位图(用于设置为imageView)时,出现EACCES(权限被拒绝)异常。

我试图用以下内容阅读它:

java.File Api

contentResolver.openInpuStream()

contentResolver.openFileDescriptor()

imagedecoder

没有任何帮助。如我所见,在清单中未启用requestlegacyexternalstorage标志的情况下,无法在android 10(目标为29 sdk)上使用文件方案读取Uri。

如果我错了,请帮助我弄清楚如何阅读。

关于它的神秘事情,具有针对29 sdk的应用的android 11模拟器可以在没有requestlegacyexternalstorage标志的情况下读取这些类型的Uri。

是的,我已授予READ / WRITE_EXTERNAL_STORAGE权限。

解决方法

一些来自Play市场的应用程序向我返回了带有文件方案的Uri,例如:file:///storage/emulated/0/Download/sample.jpg

某些应用程序是由经验不足的开发人员编写的。

如果我错了,请帮助我弄清楚如何阅读。

在Android 10上,如果没有Uri,您将无法读取该android:requestLegacyExternalStorage="true"

关于它的神秘事情,具有针对29 sdk的应用的android 11模拟器可以在没有requestlegacyexternalstorage标志的情况下读取这些类型的Uri。

Android 11中引入的“原始路径”功能意味着READ_EXTERNAL_STORAGE再次正常工作。因此,推荐的方法是请求READ_EXTERNAL_STORAGE plus 使用android:requestLegacyExternalStorage="true",以确保Android版本之间的一致性。

,

这对我来说非常有效(我已经在针对 sdk 29 的 Android 8、10、11 上对其进行了测试)。不需要 android:requestLegacyExternalStorage="true" 和任何权限。

private static Bitmap readImage(Uri uri,Context context){
   InputStream inputStream = context.getContentResolver().openInputStream(uri);
   Bitmap res = BitmapFactory.decodeStream(inputStream);
   inputStream.close();
   return res;
}

你像这样使用它:

@Override
public void onActivityResult(int requestCode,int resultCode,Intent data) {
    super.onActivityResult(requestCode,resultCode,data);
       
    if(data == null || data.getClipData().size() == 0)
       return;

    myImageView.setImageBitmap(readImage(data.getClipData().getItemAt(0).getUri(),this));
                    
}