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

Android 12下外部存储文件卸载留存及免弹窗读写方案咨询

Android 12下留存型外部存储文件读写方案(卸载不删除、无重复弹窗)

针对你的需求,以下是两种可行的实现方案,均满足文件在应用卸载后留存、无重复读写弹窗的要求:

方案一:使用MANAGE_EXTERNAL_STORAGE权限(直接操作公共存储目录)

核心逻辑

Android 11+引入的MANAGE_EXTERNAL_STORAGE权限允许应用直接读写公共外部存储的任意目录,权限只需一次性授权(引导用户到设置开启),后续读写无弹窗,且文件会留存至应用卸载后。

实现步骤

  1. 权限声明:在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
                 android:minSdkVersion="30" />
<!-- 兼容低版本的传统读写权限(Android 10+已失效,可选保留) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="29" />
  1. 权限检查与引导:判断是否已获取权限,未获取则跳转至设置页面:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    if (!Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }
}
  1. 文件读写操作:权限获取后,直接在公共存储的自定义目录下创建并读写文件:
// 创建自定义公共存储目录(卸载后不会被删除)
File appPublicDir = new File(Environment.getExternalStorageDirectory(), "MyAppPairFiles");
if (!appPublicDir.exists()) {
    appPublicDir.mkdirs();
}

// 写入文本文件
File txtFile = new File(appPublicDir, "f1.txt");
try (FileWriter writer = new FileWriter(txtFile)) {
    writer.write("测试文本内容");
} catch (IOException e) {
    e.printStackTrace();
}

// 写入关联图片(假设已有Bitmap对象)
File imgFile = new File(appPublicDir, "img1.jpg");
try (FileOutputStream fos = new FileOutputStream(imgFile)) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
} catch (IOException e) {
    e.printStackTrace();
}

// 读取文本文件示例
try (BufferedReader reader = new BufferedReader(new FileReader(txtFile))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // 处理读取内容
    }
} catch (IOException e) {
    e.printStackTrace();
}

优缺点

  • 优点:无需用户手动选择目录,代码风格与传统文件IO一致,开发成本低
  • 缺点:属于Google严格管控的特殊权限,非必要场景提交Google Play可能被拒;部分国产ROM可能存在额外权限限制

方案二:使用SAF存储访问框架(持久化目录权限)

核心逻辑

通过SAF让用户手动选择一个存储目录,获取该目录的持久化URI权限,后续读写该目录下的文件无需再次弹窗,文件卸载后留存,符合Google分区存储的推荐规范。

实现步骤

  1. 启动目录选择器:引导用户选择目标目录,并申请持久化权限:
private static final int REQUEST_CODE_PICK_DIR = 1001;

// 触发目录选择
private void pickStorageDir() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    startActivityForResult(intent, REQUEST_CODE_PICK_DIR);
}
  1. 处理选择结果并持久化权限:将选中目录的URI保存到SharedPreferences,方便后续复用:
@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();
        // 持久化权限
        getContentResolver().takePersistableUriPermission(treeUri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // 保存URI到SharedPreferences
        SharedPreferences sp = getSharedPreferences("storage_config", MODE_PRIVATE);
        sp.edit().putString("selected_dir_uri", treeUri.toString()).apply();
    }
}
  1. 使用持久化URI读写文件:从SharedPreferences读取URI后,通过DocumentFile操作文件:
// 读取保存的目录URI
SharedPreferences sp = getSharedPreferences("storage_config", MODE_PRIVATE);
String uriStr = sp.getString("selected_dir_uri", null);
if (uriStr == null) {
    // 未选择目录,重新触发选择
    pickStorageDir();
    return;
}

Uri treeUri = Uri.parse(uriStr);
DocumentFile rootDir = DocumentFile.fromTreeUri(this, treeUri);

// 创建并写入文本文件
DocumentFile txtFile = rootDir.createFile("text/plain", "f1.txt");
try (OutputStream os = getContentResolver().openOutputStream(txtFile.getUri())) {
    os.write("测试文本内容".getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

// 创建并写入关联图片
DocumentFile imgFile = rootDir.createFile("image/jpeg", "img1.jpg");
try (OutputStream os = getContentResolver().openOutputStream(imgFile.getUri())) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, os);
} catch (IOException e) {
    e.printStackTrace();
}

// 读取文本文件示例
try (InputStream is = getContentResolver().openInputStream(txtFile.getUri());
     BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // 处理读取内容
    }
} catch (IOException e) {
    e.printStackTrace();
}

优缺点

  • 优点:符合Google分区存储规范,权限审核更容易通过;用户可自主选择存储位置
  • 缺点:首次操作需要用户手动选择目录(仅一次弹窗);文件操作需通过ContentResolver,代码相对繁琐

注意事项

  • 两种方案创建的文件均会在应用卸载后留存,且不会触发重复读写弹窗
  • Android 12中,即使拥有MANAGE_EXTERNAL_STORAGE权限,也无法访问其他应用的专属外部存储目录
  • 若仅需存储媒体文件(如图片),也可通过MediaStore写入公共媒体目录(如Pictures),但无法与文本文件放在同一自定义目录下

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:13:15