Scoped Storage:无需SAF,能否通过绝对路径获取图片Uri?
我明白你的痛点——不想为了适配Scoped Storage去修改用户和服务器数据库,但直接把绝对路径转Uri调用decodeStream会触发FileNotFoundException,这是因为你用Uri.parse(path)生成的Uri没有对应的Content Provider支持,系统无法通过ContentResolver访问到文件。下面给你几种规避方案,不用大规模修改数据库就能实现获取图片宽高的需求:
方案1:兼容模式下直接用BitmapFactory.decodeFile(API 29可用)
如果你的应用targetSdkVersion < 29,或者在AndroidManifest里添加了android:requestLegacyExternalStorage="true"(API 29下有效,API 30+会被逐渐限制),可以直接用绝对路径调用decodeFile来解码图片边界,完全不用碰Uri:
// 只解码图片边界,不加载完整位图,节省内存 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 传入绝对路径直接解码 BitmapFactory.decodeFile("/storage/emulated/0/DCIM/Camera/20200128_122949_020.jpg", options); // 获取宽高 int imageWidth = options.outWidth; int imageHeight = options.outHeight;
这个方法最省心,不需要修改任何存储的路径字段,直接沿用原来的绝对路径即可。
方案2:API 30+直接用FileInputStream读取(私有目录/自有文件)
如果你的应用已经升级到targetSdkVersion >= 30,requestLegacyExternalStorage不再生效,那针对自己应用生成的图片(比如存放在应用私有目录getExternalFilesDir()下的文件),可以直接用FileInputStream来解码边界:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; File imageFile = new File("/storage/emulated/0/Android/data/your.package.name/files/photos/xxx.jpg"); try (FileInputStream fis = new FileInputStream(imageFile)) { BitmapFactory.decodeStream(fis, new Rect(), options); } catch (IOException e) { // 处理文件读取异常 e.printStackTrace(); } int imageWidth = options.outWidth; int imageHeight = options.outHeight;
这种方式绕开了ContentResolver,直接通过文件流读取,不需要Uri,也不用修改数据库。
方案3:公共目录图片通过绝对路径查询MediaStore获取Uri
如果图片是系统相机、图库等公共目录下的文件,API 30+无法直接通过绝对路径访问,这时候可以通过绝对路径去MediaStore查询对应的Content Uri,不用修改数据库存储的路径,只是在解码前做一次转换:
// 根据绝对路径获取对应的Content Uri private Uri getImageUriFromPath(Context context, String absolutePath) { Uri contentUri = null; String[] projection = {MediaStore.Images.Media._ID}; // 用绝对路径作为查询条件 String selection = MediaStore.Images.Media.DATA + " = ?"; String[] selectionArgs = {absolutePath}; Cursor cursor = context.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { long imageId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)); contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageId); cursor.close(); } return contentUri; } // 调用示例 Uri imageUri = getImageUriFromPath(context, "/storage/emulated/0/DCIM/Camera/20200128_122949_020.jpg"); if (imageUri != null) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try (InputStream is = context.getContentResolver().openInputStream(imageUri)) { BitmapFactory.decodeStream(is, new Rect(), options); int width = options.outWidth; int height = options.outHeight; // 使用宽高 } catch (IOException e) { e.printStackTrace(); } }
注意:这个方法依赖MediaStore的索引,如果图片是刚创建还没被系统扫描到,可能查询不到Uri,这时候可以考虑手动触发Media扫描,或者如果是你自己的应用保存的公共目录图片,建议保存的时候直接记录MediaStore返回的Uri。
内容的提问来源于stack exchange,提问作者whereismaxmax

