Android 10中从Bitmap获取URI的方法求助
解决Android 10中MediaStore URI获取的适配问题
我来帮你搞定这个Android 10(API 29)的适配痛点——这本质是分区存储机制引入后带来的API兼容问题,咱们一步步拆解解决:
先解决编译错误:无法识别Q、RELATIVE_PATH等常量
你遇到的无法解析Q、RELATIVE_PATH、IS_PENDING问题,90%是因为项目的编译SDK版本没升到29及以上。这些API是Android 10才新增的,低版本编译SDK根本识别不了。
打开Module级别的build.gradle,把编译和目标SDK版本调整成这样:
android { compileSdkVersion 29 // 或更高版本,比如33、34都可以 defaultConfig { targetSdkVersion 29 // 建议和compileSdkVersion保持一致 // ...其他原有配置 } }
同步项目后,那些编译错误应该就消失了。
替换废弃的insertImage方法,适配Android 10+
你原来用的MediaStore.Images.Media.insertImage在Android 10及以上已经被标记为@Deprecated,而且会触发分区存储的限制,所以必须改用ContentValues+ContentResolver的方式来插入文件并获取URI。
下面是完整的Bitmap存储并获取URI的适配代码,兼容Android 10及以下:
private Uri saveBitmapToMediaStore(Context context, Bitmap bitmap, String mimeType) { ContentValues contentValues = new ContentValues(); // 生成唯一文件名,避免重复 String fileName = System.currentTimeMillis() + ".jpg"; contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType); Uri uri = null; ContentResolver resolver = context.getContentResolver(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+:使用分区存储的相对路径,比如存在Pictures下的自定义文件夹 String relativePath = Environment.DIRECTORY_PICTURES + "/YourAppVideoThumb"; contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath); // 标记文件为"待处理",防止其他应用在写入完成前访问 contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1); // 插入到MediaStore的图片集合中,获取临时URI uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); } else { // Android 9及以下:用传统文件存储+MediaScanner扫描的方式 String storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath(); String savePath = storageDir + "/YourAppVideoThumb/" + fileName; File saveFile = new File(savePath); // 创建文件夹(如果不存在) if (!saveFile.getParentFile().exists()) { saveFile.getParentFile().mkdirs(); } // 把Bitmap写入文件 try (FileOutputStream fos = new FileOutputStream(saveFile)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.flush(); } catch (IOException e) { e.printStackTrace(); return null; } // 扫描文件到MediaStore,生成URI MediaScannerConnection.scanFile(context, new String[]{savePath}, new String[]{mimeType}, (path, scannedUri) -> { uri = scannedUri; }); // 注意:这里是异步操作,如果需要同步返回,建议加个短暂等待或者改用回调逻辑 } // Android 10+:将Bitmap写入到刚才获取的URI对应的流中 if (uri != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { try (OutputStream outputStream = resolver.openOutputStream(uri)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); outputStream.flush(); // 写入完成后,取消"待处理"标记,允许其他应用访问 ContentValues updateValues = new ContentValues(); updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0); resolver.update(uri, updateValues, null, null); } catch (IOException e) { e.printStackTrace(); // 写入失败,删除刚才插入的空记录 resolver.delete(uri, null, null); uri = null; } } return uri; }
关键知识点说明
- 分区存储规则:Android 10+不允许直接操作外部存储的文件路径,必须通过MediaStore API来读写公共目录下的文件
- IS_PENDING的作用:插入时设为1,避免其他应用在文件写入完成前读取到空文件;写入完成后设为0,开放访问权限
- 权限适配:Android 10+不需要申请
WRITE_EXTERNAL_STORAGE权限(分区存储默认允许写入公共目录的指定位置);如果你的targetSdkVersion是28及以下,仍需正常申请存储权限
如果你是要获取视频文件的URI(而不是Bitmap缩略图),只需要把代码中的MediaStore.Images.Media.EXTERNAL_CONTENT_URI换成MediaStore.Video.Media.EXTERNAL_CONTENT_URI,MIME_TYPE换成对应视频类型(比如video/mp4),相对路径换成Environment.DIRECTORY_MOVIES + "/YourAppVideo"即可。
内容的提问来源于stack exchange,提问作者Chirag Patel
相关产品推荐
相关产品推荐

