【安卓基础】一文搞懂Android历代版本文件访问权限变化

您所在的位置:网站首页 android文件限制访问 【安卓基础】一文搞懂Android历代版本文件访问权限变化

【安卓基础】一文搞懂Android历代版本文件访问权限变化

2024-06-03 23:42| 来源: 网络整理| 查看: 265

文章目录 前言1、几个目录1.1 私有目录1.1.1 api < 191.1.2 18 if (PRINT_INTERNAL_VOLUME and pathTypes != 0) { append("\n${getString(R.string.internal_storage_volume)}: ${appContext.filesDir}").append( "\t${appContext.cacheDir}" ) } if (PRINT_EXTERNAL_VOLUME_SPECIFIC and pathTypes != 0) { append( "\n${getString(R.string.external_storage_volume_specific)}: ${ appContext.getExternalFilesDir( Environment.DIRECTORY_MUSIC ) }" ).append("\t${appContext.externalCacheDir}") } if (PRINT_EXTERNAL_VOLUME_SHARE and pathTypes != 0) { append("\n${getString(R.string.external_storage_volume_share)}:${Environment.getExternalStorageDirectory()}") } } Log.d(TAG, paths.toString()) }

正常情况输出如下:

内部存储: /data/data/com.codersth.android.foundation/files /data/data/com.codersth.android.foundation/cache 外部存储(私有目录,以Music为例): /storage/sdcard/Android/data/com.codersth.android.foundation/files/Music

下面我们对不同的系统版本,再来看看上面的输出。

1.1.1 api < 19

我们创建api18的虚拟机,在不加任何存储权限的情况下,再次运行上面的代码。

内部存储: /data/data/com.codersth.android.foundation/files /data/data/com.codersth.android.foundation/cache 外部存储(私有目录): null null

可以看到,虽然没有提示权限问题,但外部存储(私有目录)拿不到具体路径。 尝试加上读取权限:

结果仍然为null。 只有加上写入权限,才是正常的。

关于这一点,我们可以在官网上得到验证。

Also starting in API level 19, this permission is not required to read/write files in your application-specific directories returned by Context.getExternalFilesDir(String) and Context.getExternalCacheDir().

也就是说,从api19开始,打印外部存储(私有目录)路径才不需要WRITE_EXTERNAL_STORAGE这个权限。

1.1.2 18 val file = File(filesDir, fileName) Log.d(TAG, "${getString(R.string.internal_storage_volume)}: read file content: ${readFileContent(file)}") } if (PRINT_EXTERNAL_VOLUME_SPECIFIC and pathTypes != 0) { val file = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName) Log.d(TAG, "${getString(R.string.external_storage_volume_specific)}: read file content: ${readFileContent(file)}") } if (PRINT_EXTERNAL_VOLUME_SHARE and pathTypes != 0) { val file = File(Environment.getExternalStorageDirectory(), fileName) Log.d(TAG, "${getString(R.string.external_storage_volume_share)}: read file content: ${readFileContent(file)}") } }

我们事先分别在三个目录中上传文件。 在这里插入图片描述 然后执行:

readStorageUnChecked(PRINT_INTERNAL_VOLUME or PRINT_EXTERNAL_VOLUME_SPECIFIC or PRINT_EXTERNAL_VOLUME_SHARE)

结果正常,都输出了文件中的内容:

2022-02-10 06:16:03.422 7725-7725/com.codersth.android.foundation D/FSMainActivity: 内部存储: read file content: hello, android developer 2022-02-10 06:16:03.422 7725-7725/com.codersth.android.foundation D/FSMainActivity: 外部存储(私有目录): read file content: hello, android developer 2022-02-10 06:16:03.422 7725-7725/com.codersth.android.foundation D/FSMainActivity: 外部存储(共享目录): read file content: hello, android developer

所以说在api < 19的情况下,不用任何权限均可以正常读取文件。

2.1.2 写文件

测试代码如下:

/** * 在不同的目录下创建文件 * @param pathTypes 打印的路径类型 * @see [PRINT_EXTERNAL_VOLUME_SHARE][PRINT_EXTERNAL_VOLUME_SPECIFIC][PRINT_EXTERNAL_VOLUME_SHARE] */ private fun writeStorageUnChecked(pathTypes: Int) { if (PRINT_INTERNAL_VOLUME and pathTypes != 0) { val file = File(filesDir, "${System.currentTimeMillis()}.txt") Log.d(TAG, "${getString(R.string.internal_storage_volume)}: create file ${file.createNewFile()}, file path: ${file.absolutePath}") } if (PRINT_EXTERNAL_VOLUME_SPECIFIC and pathTypes != 0) { val file = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "${System.currentTimeMillis()}.txt") Log.d(TAG, "${getString(R.string.external_storage_volume_specific)}: create file ${file.createNewFile()}, file path: ${file.absolutePath}") } if (PRINT_EXTERNAL_VOLUME_SHARE and pathTypes != 0) { val file = File(Environment.getExternalStorageDirectory(), "${System.currentTimeMillis()}.txt") Log.d(TAG, "${getString(R.string.external_storage_volume_share)}: create file ${file.createNewFile()}, file path: ${file.absolutePath}") } }

我们先在内部存储中创建文件:

accessStorageUnChecked(PRINT_INTERNAL_VOLUME)

再看下目录里,文件确实存在。 在这里插入图片描述 接在我们在外部存储(私有目录)中创建文件,可以看到报错了。

libcore.io.ErrnoException: open failed: EROFS (Read-only file system)

我们加入WRITE_EXTERNAL_STORAGE权限再试下。

2022-02-10 05:31:36.302 5737-5737/com.codersth.android.foundation D/FSMainActivity: 外部存储(私有目录): create file true, file path: /storage/sdcard/Android/data/com.codersth.android.foundation/files/Download/1644471096304.txt

外部存储共享目录同样会报错,大家不妨自己试下。 所以说在api < 19的情况下写文件,内部存储不用权限,外部存储需要WRITE_EXTERNAL_STORAGE。

2.2 19 act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.codersth.android.foundation/.filesystem.FSMainActivity} from uid 2000 2022-02-10 16:19:29.636 8543-8543/com.codersth.android.foundation D/FSMainActivity: 内部存储: create file true, file path: /data/user/0/com.codersth.android.foundation/files/1644481169636.txt 2022-02-10 16:19:29.638 8543-8543/com.codersth.android.foundation D/FSMainActivity: 外部存储(私有目录): create file true, file path: /storage/emulated/0/Android/data/com.codersth.android.foundation/files/Download/1644481169638.txt 2022-02-10 16:19:29.639 8543-8543/com.codersth.android.foundation D/FSMainActivity: 外部存储(共享目录): create file true, file path: /storage/emulated/0/1644481169639.txt

值得一提的是,在实际的开发过程中,我们仍然以官网建议为准备,即尽量从私有目录中存储,除非应用实在需要访问共享目录。

结论:私有目录不用任何权限即可读写文件,共享目录读写时分别赋予READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE仍没卵用,可通过requestLegacyExternalStorage = true来暴力解决这个问题。 自此,WRITE_EXTERNAL_STORAGE在api >= 29上将寿终正寝。

2.5 29 < api

如果说api29及以下可以通过requestLegacyExternalStorage = true来解决共享目录存储问题的话,api = 30开始,google则彻底堵死了这个口子。 在这里插入图片描述 我们看到,尽管上面该加的都加了,仍然会报错:

Caused by: java.io.IOException: Operation not permitted at java.io.UnixFileSystem.createFileExclusively0(Native Method) at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:317)

也就是说api29(Android 11)开始,google是逼着你用分区存储模型了。 那么api > 29该如何使用共享目录呢? 首先,在清单文件中声明权限:

然后,增加对Android R的权限处理。

// 判断当前应用是否管理外部存储的权限 if(Environment.isExternalStorageManager()) { writeStorageUnChecked(pathTypes) } else { // 赋予权限 val forResultLauncher = registerForActivityResult(ManagerStorageContract() ) { if (Environment.isExternalStorageManager()) { writeStorageUnChecked(pathTypes) } else { Toast.makeText(this, "Permission not granted.", Toast.LENGTH_SHORT).show(); } } forResultLauncher.launch(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) }

这样,在访问文件前,会进入这样的页面,用户开启则表示该应用有管理共享目录的权限。 在这里插入图片描述 这样便成功写入文件了。

2022-02-10 17:21:00.103 8165-8165/com.codersth.android.foundation D/FSMainActivity: 内部存储: create file true, file path: /data/user/0/com.codersth.android.foundation/files/1644484860103.txt 2022-02-10 17:21:00.122 8165-8165/com.codersth.android.foundation D/FSMainActivity: 外部存储(私有目录): create file true, file path: /storage/emulated/0/Android/data/com.codersth.android.foundation/files/Download/1644484860121.txt 2022-02-10 17:21:00.126 8165-8165/com.codersth.android.foundation D/FSMainActivity: 外部存储(共享目录): create file true, file path: /storage/emulated/0/1644484860124.txt

读文件同理,不再赘述。 结论:对于api 30及以上版本,私有目录可正常访问,但是共享目录需要用户在明确自己应用用途的情况下设置中开启“访问外部存储”的开关。

3、总结

以上便介绍完了Android历代版本中文访问权限变化,可以看到,单纯访问文件这件事,在个别版本中都千差万别。 在实际开发过程中,强烈建议大家遵守google开发规范,合理存放自己的数据。

仓库地址:https://github.com/codersth/android-foundation-samples/blob/master/app/src/main/java/com/codersth/android/foundation/filesystem/FSMainActivity.kt



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3