You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 端实现

  1. 创建自定义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);
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:09:54