Android 11 Storage Access Framework批量复制媒体文件到Pictures目录咨询
可行实现方案
不需要每次复制都申请权限的实现方式有两种,可根据你的业务场景选择:
- 分区存储适配方案(官方推荐,无需高危权限)
仅需一次授权即可长期使用:- 清单文件声明对应权限:
- 目标SDK >=33:声明
READ_MEDIA_IMAGES权限 - 目标SDK 29~32:声明
READ_EXTERNAL_STORAGE权限
- 目标SDK >=33:声明
- 拿到源文件Uri后,调用
context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)获取该Uri的长期读取权限,后续无需再针对该文件申请权限 - 写入Pictures目录时改用MediaStore API,不需要额外申请写入权限,分区存储下App写入自己创建的公共媒体文件无需权限
- 清单文件声明对应权限:
- 全存储权限方案
申请MANAGE_EXTERNAL_STORAGE权限,用户授权一次后即可自由读写所有公共存储路径,包括Android/media和Pictures目录,但该权限属于高危权限,上架应用商店需要提交使用场景说明通过审核。
修正后的写入代码示例
你原有代码直接用File操作公共目录在API29以上已经失效,改用MediaStore的实现如下:
public void fileToPictures(Context context, String filename, Bitmap image) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10及以上用MediaStore写入 ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, filename + ".png"); values.put(MediaStore.Images.Media.MIME_TYPE, "image/png"); values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); values.put(MediaStore.Images.Media.IS_PENDING, 1); ContentResolver resolver = context.getContentResolver(); Uri insertUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); if (insertUri == null) { Toast.makeText(context, "Sticker couldn't be saved. Error: 创建文件失败", Toast.LENGTH_SHORT).show(); return; } try { OutputStream outputStream = resolver.openOutputStream(insertUri); if (outputStream != null) { image.compress(Bitmap.CompressFormat.PNG, 100, outputStream); outputStream.flush(); outputStream.close(); } values.clear(); values.put(MediaStore.Images.Media.IS_PENDING, 0); resolver.update(insertUri, values, null, null); Toast.makeText(context, "Sticker saved in pictures as: " + filename, Toast.LENGTH_SHORT).show(); } catch (IOException e) { Toast.makeText(context, "Sticker couldn't be saved. Error: " + e.getMessage(), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } else { // Android 9及以下可以继续用原有File方式 try { File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File file = new File(directory, filename + ".png"); FileOutputStream outputStream = new FileOutputStream(file); image.compress(Bitmap.CompressFormat.PNG, 100, outputStream); outputStream.flush(); outputStream.getFD().sync(); outputStream.close(); MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null); Toast.makeText(context, "Sticker saved in pictures as: " + filename, Toast.LENGTH_SHORT).show(); } catch (IOException e) { Toast.makeText(context, "Sticker couldn't be saved. Error: " + e.getMessage(), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } }
额外优化建议
如果只是单纯复制文件不需要修改图片内容,不需要转Bitmap再压缩,直接读取源Uri的输入流,写入到MediaStore返回的输出流即可,既能避免画质损失,也能提升复制效率。
内容的提问来源于stack exchange,提问作者Marvin Stelter
相关产品推荐
相关产品推荐

