调用系统文件夹选择器选择下载路径时如何避免触发权限请求?
解决每次选择下载文件夹重复弹出权限请求的问题
咱们先来理清楚问题根源:你虽然申请了WRITE_EXTERNAL_STORAGE权限,但从Android 10(API 29)开始的Scoped Storage机制彻底改变了存储权限逻辑——这个权限现在仅能访问媒体文件(图片、音频、视频等),而通过ACTION_OPEN_DOCUMENT_TREE打开的文件夹属于Storage Access Framework (SAF) 范畴,它的权限是基于URI的临时权限,默认每次选择都会重新请求,和你申请的传统存储权限完全是两回事。
要避免重复弹出权限请求,核心是持久化用户选择的文件夹URI及其权限,这样下次启动应用时直接复用即可。下面是具体的实现步骤:
1. 持久化SAF权限
当用户第一次选择完文件夹后,调用ContentResolver.takePersistableUriPermission()把这个URI的读写权限持久化下来,系统就不会每次都要求用户确认权限了。
用AndroidX的ActivityResultContracts实现(官方推荐)
现在Android官方推荐用ActivityResultContracts替代旧的onActivityResult,代码更简洁且生命周期安全:
// 注册文件夹选择的Launcher private val openDocumentTreeLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { selectedUri -> selectedUri?.let { uri -> // 持久化读写权限 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) // 把URI保存到SharedPreferences,方便下次启动复用 val prefs = getSharedPreferences("AppStoragePrefs", MODE_PRIVATE) prefs.edit() .putString("saved_download_dir", uri.toString()) .apply() // 这里就可以用这个URI进行下载操作了 } } // 启动文件夹选择器的触发代码 fun openDownloadFolderSelector() { openDocumentTreeLauncher.launch(null) }
2. 下次启动时复用已保存的URI
应用重启后,先读取之前保存的URI,检查权限是否有效,有效就直接使用,不用再打开选择器:
fun initDownloadDir() { val prefs = getSharedPreferences("AppStoragePrefs", MODE_PRIVATE) val savedUriStr = prefs.getString("saved_download_dir", null) if (savedUriStr != null) { val savedUri = Uri.parse(savedUriStr) // 检查权限是否仍然有效 val hasWritePermission = contentResolver.checkUriPermission( savedUri, Process.myPid(), Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) == PackageManager.PERMISSION_GRANTED if (hasWritePermission) { // 直接使用这个URI进行下载操作,无需再次打开选择器 useSavedDownloadDir(savedUri) } else { // 权限失效,重新打开选择器 openDownloadFolderSelector() } } else { // 没有保存的URI,首次打开选择器 openDownloadFolderSelector() } } // 自定义方法:使用保存的URI进行下载操作 private fun useSavedDownloadDir(uri: Uri) { // 在这里实现你的下载逻辑,比如创建文件、写入数据等 }
关键注意点
- 持久化的权限会一直保留,直到用户在系统设置中撤销应用对该文件夹的访问权限,或者应用被卸载。
- 如果用户选择的是系统默认的Download目录,你也可以考虑用
MediaStore.Downloads来操作,但如果是自定义文件夹,SAF的方式更可靠。 - 从Android 11(API 30)开始,
WRITE_EXTERNAL_STORAGE权限已经被标记为deprecated,建议完全转向SAF或MediaStore来处理文件操作。
内容的提问来源于stack exchange,提问作者Ильмир Шагабиев




