Android 9及以下系统如何申请特定目录权限?
Android 9及以下系统申请特定目录权限的问题解答
核心结论
Android 9(API 28及以下)无法通过ACTION_OPEN_DOCUMENT_TREE指定初始目录,因为DocumentsContract.EXTRA_INITIAL_URI参数是从Android 10(API 29)才开始正式支持的,你当前代码里的该参数在Android9上完全无效,系统会默认打开最近访问的目录。
原因分析
Android 10之前采用传统外部存储模型:只要申请了READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE权限,就能直接访问外部存储上的任意目录(包括WhatsApp状态目录),不需要依赖文档选择器。而ACTION_OPEN_DOCUMENT_TREE在Android9及以下仅能打开文档树,但不支持指定初始路径,这是系统API的硬性限制。
适配方案
方案1:直接申请存储权限(推荐)
对于Android9及以下,直接申请存储权限即可直接访问目标目录,无需文档选择器:
fun openDirectory() { if (Build.VERSION.SDK_INT >= 29) { // Android 10+ 沿用原文档选择器逻辑 val pickerInitialPath = "${Environment.getExternalStorageDirectory().absolutePath}$WS_DIRECTORY_PATH" val folderUri = Uri.parse(pickerInitialPath) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { putExtra(DocumentsContract.EXTRA_INITIAL_URI, folderUri) } startActivityForResult(intent, 2000) } else { // Android 9及以下 申请存储权限 val readPermission = Manifest.permission.READ_EXTERNAL_STORAGE if (ContextCompat.checkSelfPermission(this, readPermission) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(readPermission), 1000) } else { // 权限已授予,直接访问目录 accessTargetDirectory() } } } private fun accessTargetDirectory() { val statusDir = File(Environment.getExternalStorageDirectory(), WS_DIR_OLDER_PATH) if (statusDir.exists() && statusDir.isDirectory) { // 此处处理目录内容,比如读取文件列表 val files = statusDir.listFiles()?.filter { it.isFile } // ... } } // 处理权限申请结果 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == 1000 && grantResults.isNotEmpty()) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { accessTargetDirectory() } else { // 权限被拒绝,提示用户开启权限以访问目标目录 } } }
方案2:保留文档选择器(不推荐)
如果一定要用文档选择器,Android9及以下无法指定初始目录,只能引导用户手动找到/WhatsApp/Media/.Statuses目录,原onActivityResult逻辑可以继续使用,但体验较差:
// Android9及以下的文档选择器调用逻辑 fun openDirectoryForOlderDevices() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) startActivityForResult(intent, 2000) } // 原onActivityResult逻辑不变 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) try { if (requestCode == 2000 && resultCode == Activity.RESULT_OK) { data?.data?.also { uri -> val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION contentResolver.takePersistableUriPermission(uri, takeFlags) // 保存URI,后续通过ContentResolver访问目录 } } } catch (ex: Exception) { Log.d("OnActivity failure", "ON_ACTIVITY_RESULT FAILED WITH EXCEPTION $ex") } }
注意事项
- Android9及以下的存储权限申请需要在
AndroidManifest.xml中声明:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 如果需要写入权限,添加下面一行 --> <!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> --> - 对于隐藏目录(比如
.Statuses),Android9及以下默认允许访问,无需额外配置。
内容的提问来源于stack exchange,提问作者shyam__
相关产品推荐
相关产品推荐

