Android 7下跨App外部存储写入权限问题:App A无法写入App B存储
Android 7 跨App写入外部私有存储解决方案与权限说明
权限说明
- WRITE_EXTERNAL_STORAGE 权限限制:在Android 7(API 24)及以上版本,该权限仅允许应用访问公共外部存储(如Download、DCIM目录)和自身的私有外部存储目录(
Android/data/自身包名路径)。其他App的私有外部存储目录受沙箱保护,即便持有该权限也无法直接读写。 - 文件管理器的特殊权限:你观察到的SD卡访问弹窗,是存储访问框架(SAF)的目录授权请求。文件管理器通过SAF请求用户授予特定目录或整个SD卡的读写权限,从而突破App沙箱限制,访问其他App的私有存储目录。
解决方案
方案1:通过SAF请求用户授权(通用合规方案)
通过存储访问框架引导用户手动选择并授权App B的files目录,获取授权后即可通过ContentResolver操作该目录下的文件:
private static final int REQUEST_CODE_PICK_DIR = 1001; // 启动SAF目录选择器 private void requestAppBDirAccess() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_CODE_PICK_DIR); } // 处理授权结果 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_PICK_DIR && resultCode == RESULT_OK) { Uri treeUri = data.getData(); // 持久化授权,避免App重启后权限失效 getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 执行文件移动操作 moveFileToAppB(treeUri); } } // 移动App A的文件到授权后的App B目录 private void moveFileToAppB(Uri appBDirUri) { File appAFile = new File(getDownloadDirPath().getAbsolutePath() + "/target_file.txt"); if (!appAFile.exists()) return; DocumentFile appBDir = DocumentFile.fromTreeUri(this, appBDirUri); DocumentFile destFile = appBDir.createFile("*/*", appAFile.getName()); if (destFile == null) return; try (InputStream is = new FileInputStream(appAFile); OutputStream os = getContentResolver().openOutputStream(destFile.getUri())) { byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } // 移动完成后删除原文件 appAFile.delete(); } catch (IOException e) { e.printStackTrace(); } }
方案2:跨App进程通信(推荐,适用于自研多App场景)
若两个App均为你开发,可通过ContentProvider实现App B主动接收文件,无需用户手动授权:
App B 端实现
- 创建自定义ContentProvider:
public class AppBFileProvider extends ContentProvider { @Override public boolean onCreate() { return true; } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { String fileName = uri.getLastPathSegment(); File destFile = new File(getContext().getExternalFilesDir(null), fileName); // 确保目录存在 destFile.getParentFile().mkdirs(); return ParcelFileDescriptor.open(destFile, ParcelFileDescriptor.MODE_WRITE_ONLY); } }
- 在
AndroidManifest.xml中注册:
<provider android:name=".AppBFileProvider" android:authorities="com.test.appB.fileprovider" android:exported="true" android:grantUriPermissions="true"/>
App A 端调用
private void moveFileToAppBViaProvider() { File appAFile = new File(getDownloadDirPath().getAbsolutePath() + "/target_file.txt"); if (!appAFile.exists()) return; Uri destUri = Uri.parse("content://com.test.appB.fileprovider/" + appAFile.getName()); try (InputStream is = new FileInputStream(appAFile); OutputStream os = getContentResolver().openOutputStream(destUri)) { byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } appAFile.delete(); } catch (IOException e) { e.printStackTrace(); } }
方案3:全局SD卡权限(不推荐)
部分厂商的Android 7系统支持android.permission.WRITE_MEDIA_STORAGE权限,该权限可获得SD卡全局读写能力,但属于系统级权限,普通App无法通过常规权限申请流程获取,需用户手动在系统设置中开启,兼容性极差,不建议采用。
内容的提问来源于stack exchange,提问作者user22506935
相关产品推荐
相关产品推荐

