Android应用强制关闭后丢失文件夹永久访问权限问题
问题根源
- URI权限未持久化:通过
ACTION_OPEN_DOCUMENT_TREE获取的访问权限默认是临时的,应用进程被强制关闭后会自动失效。你未调用takePersistableUriPermission将权限持久化,导致重启后无法访问之前选中的文件夹。 - 缺少空值校验:代码里直接假设
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
相关产品推荐
相关产品推荐

