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

Android 11获取文件持久化权限后跨会话仍无法读写问题

问题描述

我希望让用户选择文件,实现代码如下:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.setType("*/*");
startActivityForResult(intent, Aux.ACTIVITY_REQUEST_CODE_SELECT_CCRYPT_ONLY);

在Activity返回时执行以下代码:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode,resultCode,data);

    if (resultCode == RESULT_OK){
        if (requestCode == Aux.ACTIVITY_REQUEST_CODE_SELECT_CCRYPT_ONLY){

            Uri dataFileUri = data.getData();

            Aux.DbugCcrypt("Storing the URI of the Work Directory In Shared Preferences: " + dataFileUri.toString());
            Aux.storeStringInSharedPreferences(this,Aux.SHARED_PREF_STRING_KEY,dataFileUri.toString());

            // And now we take persistent permission.
            ContentResolver cr = this.getContentResolver();
            cr.takePersistableUriPermission(dataFileUri, (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION));

        }
    }
    else {
        // TODO Handle.
        Aux.DbugCcrypt("On Activity Result was not OK.");
    }
}

我将URI字符串存入SharedPreferences并获取文件的持久化权限,随后关闭并重新打开应用,执行以下代码测试读写权限:

DbugCcrypt("Getting the Shared Preference URI");
String dataAuxUriString = getStringInSharedPreferences(context,SHARED_PREF_STRING_KEY);
if (!dataAuxUriString.isEmpty()){
    DbugCcrypt("Got the URI File. Testing Read/Write" );
    Uri dataAuxUri= Uri.parse(dataAuxUriString);
    File testFile = new File(dataAuxUri.getPath());
    if (testFile.isDirectory()){
        DbugCcrypt("Is a directory");
    }
    else {
        DbugCcrypt("Data Aux File URI. Can Read: " + testFile.canRead());
        DbugCcrypt("Data Aux File URI. Can Write: " + testFile.canWrite());
    }
}
else {
    DbugCcrypt("Data Aux URI String is empty");
}

得到如下日志输出(DbugCcrypt用于打印日志):

[CCRYPT_DBUG] Getting the Shared Preference URI
[CCRYPT_DBUG] Got the URI File. Testing Read/Write
[CCRYPT_DBUG] Data Aux File URI. Can Read: false
[CCRYPT_DBUG] Data Aux File URI. Can Write: false

请问为何我无法在应用会话间获取该文件的读写权限?


问题原因与解决方案

核心问题

你用File类直接处理ACTION_OPEN_DOCUMENT返回的Uri是错误的。这类Uri是内容Uri(通常以content://开头),并非本地文件系统的路径,File类无法直接识别和访问,自然会返回canRead()和canWrite()为false。

另外,虽然你获取了持久化Uri权限,但必须通过ContentResolver来操作这个Uri,而不是绕到File类。

正确的权限验证与操作方式

要验证权限并读写文件,应该使用ContentResolver提供的方法,而不是File类:

  1. 验证权限:可以通过检查ContentResolver是否能打开输入/输出流来判断权限是否有效:
DbugCcrypt("Getting the Shared Preference URI");
String dataAuxUriString = getStringInSharedPreferences(context,SHARED_PREF_STRING_KEY);
if (!dataAuxUriString.isEmpty()){
    DbugCcrypt("Got the URI File. Testing Read/Write" );
    Uri dataAuxUri= Uri.parse(dataAuxUriString);
    ContentResolver resolver = context.getContentResolver();

    // 测试读权限
    boolean canRead = false;
    try (InputStream is = resolver.openInputStream(dataAuxUri)) {
        canRead = (is != null);
    } catch (IOException e) {
        canRead = false;
    }

    // 测试写权限
    boolean canWrite = false;
    try (OutputStream os = resolver.openOutputStream(dataAuxUri)) {
        canWrite = (os != null);
    } catch (IOException e) {
        canWrite = false;
    }

    DbugCcrypt("Data Aux File URI. Can Read: " + canRead);
    DbugCcrypt("Data Aux File URI. Can Write: " + canWrite);
}
else {
    DbugCcrypt("Data Aux URI String is empty");
}
  1. 额外注意点:
    • 你在takePersistableUriPermission时同时申请了读写权限,但要确保用户选择文件时,你的Intent也声明了需要写权限。可以在启动选择Intent时添加intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);,这样系统会同时授予读写的临时权限,后续才能成功获取持久化的读写权限。
    • 部分文件管理器可能不支持授予写权限,这种情况下openOutputStream会抛出异常,需要做好错误处理。

内容的提问来源于stack exchange,提问作者aarelovich

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:42:45