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

Android应用强制关闭后丢失文件夹永久访问权限问题

问题根源
  1. URI权限未持久化:通过ACTION_OPEN_DOCUMENT_TREE获取的访问权限默认是临时的,应用进程被强制关闭后会自动失效。你未调用takePersistableUriPermission将权限持久化,导致重启后无法访问之前选中的文件夹。
  2. 缺少空值校验:代码里直接假设dfBeforeFinalDir、dfFinalDir这些对象一定不为null,但权限失效或目标文件夹被删除时,这些对象会变成null,直接调用方法就触发了空指针异常。
修复步骤

1. 持久化URI访问权限

在onActivityResult拿到URI后,必须调用takePersistableUriPermission保留权限:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    super.onActivityResult(requestCode, resultCode, resultData);
    if (requestCode == AppConstants.GRANT_ACCESS_TO_AUTO_BACKUP_FILE && resultCode == Activity.RESULT_OK) {
        if (resultData != null) {
            Uri uri = resultData.getData();
            // 核心操作:持久化权限
            int takeFlags = resultData.getFlags() 
                    & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            getContentResolver().takePersistableUriPermission(uri, takeFlags);
            
            spEditor.putString("auto_backup_uri", uri.toString()).apply();

            DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri);

            try {
                DocumentFile dfFinalDir = documentFile.findFile(getString(R.string.app_name_no_space));
                if (dfFinalDir == null) {
                    dfFinalDir = documentFile.createDirectory(getString(R.string.app_name_no_space));
                }
                if (dfFinalDir != null) {
                    Util.initConfirmDialogNoNegButton(this, getString(R.string.you_can_do_with_auto_backup));
                }
            } catch (Exception e) {
                Util.doWhenException(this, e, "The problem with /" + getString(R.string.app_name_no_space) + "/ folder: " + e.getMessage());
            }
        }
    }
}

2. 补充空值判断,避免空指针

在writeDataToFile里,对所有可能为null的DocumentFile对象做校验,提前处理异常情况:

private void writeDataToFile(Intent intent) {
    String uriString = sp.getString("auto_backup_uri", "");

    if (uriString.equals("")) {
        Util.initConfirmDialog(this, getString(R.string.choose_folder_for_auto_backup) + getString(R.string.app_name_no_space) + "/", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                Log.i("TAG", "Trying to get access the desired folder");
                Intent intent2 = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
                startActivityForResult(intent2, AppConstants.GRANT_ACCESS_TO_AUTO_BACKUP_FILE);
            }
        });
        return; // 加return,避免后续逻辑继续执行
    }

    Log.i("TAG", "Trying to write data to file " + uriString);
    Uri uri = Uri.parse(uriString);
    DocumentFile dfBeforeFinalDir = DocumentFile.fromTreeUri(this, uri);
    
    // 校验根文件夹是否可访问
    if (dfBeforeFinalDir == null) {
        doWhenException(new NullPointerException("无法访问目标文件夹"), "目标文件夹权限失效或已被删除", intent);
        return;
    }

    DocumentFile dfFinalDir = dfBeforeFinalDir.findFile(getString(R.string.app_name_no_space));
    
    // 子文件夹不存在则尝试创建,创建失败直接返回
    if (dfFinalDir == null) {
        Log.i("TAG", "应用子文件夹不存在,尝试创建");
        dfFinalDir = dfBeforeFinalDir.createDirectory(getString(R.string.app_name_no_space));
        if (dfFinalDir == null) {
            doWhenException(new NullPointerException("无法创建应用子文件夹"), "创建子文件夹失败", intent);
            return;
        }
    }

    // 现在dfFinalDir已确保非空,不会触发空指针
    DocumentFile dfFinalAutoBackupFile = dfFinalDir.findFile("notatki_a");

    if (dfFinalAutoBackupFile == null) {
        Log.i("TAG", "Auto backup file does not exist, creating new one");
        dfFinalAutoBackupFile = dfFinalDir.createFile("application/*", "notatki_a");
        // 校验文件创建是否成功
        if (dfFinalAutoBackupFile == null) {
            doWhenException(new NullPointerException("无法创建备份文件"), "创建备份文件失败", intent);
            return;
        }
    }

    OutputStream fileOutputStream = null;
    try {
        fileOutputStream = getContentResolver().openOutputStream(dfFinalAutoBackupFile.getUri());
        fileOutputStream.write(jsonNotes.getBytes(StandardCharsets.UTF_8));
        fileOutputStream.close();
        intent.putExtra("auto_save_status", AppConstants.SUCCESS);
    } catch (FileNotFoundException e) {
        doWhenException(e, "无法打开指定URI", intent);
    } catch (IOException e) {
        doWhenException(e, "写入文件时发生IO异常", intent);
    } finally {
        if (extNoteOrder > 0)
            intent.putExtra("ext_note_order", extNoteOrder);
        startActivity(intent);
    }
}

额外说明

  • 权限持久化后,只有用户通过系统设置手动撤销,应用才会失去该文件夹的访问权限。
  • 即便权限存在,用户手动删除目标文件夹或子文件夹时,findFile仍会返回null,所以空值判断逻辑不能省略。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:05:43