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

Android Studio无法打开txt到EditText:权限拒绝错误持续触发

问题根源

从Android 10(API 29)开始,系统启用分区存储(Scoped Storage),默认禁止直接通过file:// URI或文件路径访问外部存储的文件——哪怕你申请了READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE权限。你存入SQLite的应该是普通文件路径或file:// URI,这类URI没有持久访问权限,重启应用或系统后就会触发权限拒绝错误。

可行解决方案

方案1:用MediaStore API访问DCIM目录文件

DCIM属于系统媒体目录,可通过MediaStore直接查询、访问,API 30+无需READ_EXTERNAL_STORAGE权限,低版本仍需申请:

  • 查询获取目标txt文件的content:// URI(存入SQLite的应该是这个URI):
    String selection = MediaStore.Files.FileColumns.DISPLAY_NAME + " = ? AND " +
                        MediaStore.Files.FileColumns.RELATIVE_PATH + " = ?";
    String[] selectionArgs = {"目标文件名.txt", "DCIM/sheet_music/"};
    Cursor cursor = getContentResolver().query(
        MediaStore.Files.getContentUri("external"),
        new String[]{MediaStore.Files.FileColumns._ID},
        selection,
        selectionArgs,
        null
    );
    if (cursor != null && cursor.moveToFirst()) {
        long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));
        Uri contentUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id);
        // 将contentUri存入SQLite,后续用它访问文件
        cursor.close();
    }
    
  • 通过contentUri读取文件内容到EditText:
    try (InputStream is = getContentResolver().openInputStream(contentUri);
         BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        editText.setText(sb.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • 写入文件同理,调用getContentResolver().openOutputStream(contentUri)获取输出流即可。

方案2:迁移文件到应用私有外部存储

如果这些txt文件仅属于你的应用,可把它们移到应用私有外部目录,这里的文件无需任何权限就能直接访问,路径也能安全存入SQLite:

  • 迁移文件示例:
    File sourceFile = new File(Environment.getExternalStorageDirectory(), "DCIM/sheet_music/目标文件.txt");
    File destDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
    if (destDir != null) {
        File destFile = new File(destDir, "sheet_music/目标文件.txt");
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }
        // 复制文件(需处理IO异常)
        Files.copy(sourceFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        // 后续用destFile的路径直接访问,存入SQLite
    }
    

方案3:获取目录持久权限(保留原存储位置)

如果必须保留文件在DCIM/sheet_music目录,可通过一次目录选择获取持久访问权限,之后无需重复选择:

  • 启动目录选择器:
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(intent, REQUEST_CODE_TREE);
    
  • 在onActivityResult中获取目录URI并持久化权限:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_TREE && resultCode == RESULT_OK) {
            Uri treeUri = data.getData();
            // 持久化权限,重启应用仍有效
            getContentResolver().takePersistableUriPermission(treeUri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            // 将treeUri存入SharedPreferences或SQLite
        }
    }
    
  • 后续通过DocumentFile访问目录下的文件:
    DocumentFile treeDoc = DocumentFile.fromTreeUri(this, treeUri);
    if (treeDoc != null) {
        DocumentFile targetFile = treeDoc.findFile("目标文件.txt");
        if (targetFile != null) {
            Uri fileUri = targetFile.getUri();
            // 用fileUri读取/写入文件,方法同方案1
        }
    }
    
关键注意点
  • 不要使用MANAGE_EXTERNAL_STORAGE权限,该权限仅开放给文件管理器类应用,普通应用使用会被Google Play限制上架。
  • 存入SQLite的必须是content://类型的URI(而非file://或本地路径),且需确保URI有持久访问权限:方案1的MediaStore URI天然持久,方案3的目录URI需调用takePersistableUriPermission。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:43:18