1. 背景
手机系统 android11
android10之前可以这么写 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
android10、特别是android11强制要求建立应用的私有存储区域,导致访问失败
2. 先说结论
android11开始,开发者可以操作的目录,主要是 /storage/emulated/0/Android/data/<包名>/,如果要操作公共存储区(下边附一张图说明那些是公共存储区),比如相册,需要申请多媒体权限,可以保存照片到系统相册(/storage/emulated/0/),其他的的公共存储目录没试过;还有一种比较暴力的权限<MANAGE_EXTERNAL_STORAGE>,比较适宜用手机备份之类的,常规app这个权限不建议申请,有被市场打回来的风险
3. 好处
限制开发者创建的目录只能是在对应的沙盒里面,数据隔离层做了规范,当应用卸载或者清除数据后,该区域文件会被删除,不再像以前,会残留好多不需要的数据到本地
4. 关于目录的一些小知识,有踩坑的地方
/storage/emulated/0/ 对应的是手机->文件管理-> 内部存储空间
/storage/emulated/0/Android/ 有些手机这么目录进不去(踩坑),需要借助一些三方app, 比如下载一个'ES文件浏览器',他去获取访问权限,用他来查看
/data/user/0/<包名> 是一个系统的目录,貌似拿不到权限(附一张手机根目录图),读写没试过
目录打印
函数 | 对应的目录地址 |
---|---|
cacheDir | /data/user/0/<包名>/cache |
dataDir | /data/user/0/<包名> |
filesDir | /data/user/0/<包名>/files |
codeCacheDir | /data/user/0/<包名>/code_cache |
noBackupFilesDir | /data/user/0/<包名>/no_backup |
obbDir | /storage/emulated/0/Android/obb/<包名> |
externalCacheDir | /storage/emulated/0/Android/data/<包名>/cache |
externalFilesDir | /storage/emulated/0/Android/data/<包名>/files/Download |
5. 正确的读写
- 只在外部存储的应用私有目录下,用直接路径读写文件
- 访问或者共享媒体文件,使用MediaStore在公共目录下读写文件
- 访问或者共享非媒体文件,使用系统的文件选择器SAF在公共目录Download下读写文件
参考:https://www.jianshu.com/p/87d4d4071255
6. 系统的文件选择器SAF 简绍使用
- 什么是SAF
Android 4.4 就引入了存储访问框架 (SAF)。借助 SAF,用户可轻松在其所有首选文档存储提供程序中浏览并打开文档、图像及其他文件。用户可通过易用的标准界面,以统一方式在所有应用和提供程序中浏览文件,以及访问最近使用的文件。
- 使用
打开文件(Intent.ACTION_OPEN_DOCUMENT)
//通过系统的文件浏览器选择一个文件
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
//筛选,只显示可以“打开”的结果,如文件(而不是联系人或时区列表)
intent.addCategory(Intent.CATEGORY_OPENABLE)
//过滤只显示图像类型文件
intent.type = "image/*"
startActivityForResult(intent, 10)
private val IMAGE_PROJECTION = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media._ID
)
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
if (requestCode == 10) {
// 获取选择文件Uri
val uri = resultData?.data ?: return
// 获取图片信息
val cursor = this.contentResolver.query(uri, IMAGE_PROJECTION, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val displayName: String =
cursor.getString(cursor.getColumnIndexOrThrow(IMAGE_PROJECTION[0]))
val size: String =
cursor.getString(cursor.getColumnIndexOrThrow(IMAGE_PROJECTION[1]))
Log.e(mTag, "Uri: $uri")
Log.e(mTag, "Name: $displayName")
Log.e(mTag, "Size: $size")
}
cursor?.close()
}
}
创建文件(/storage/emulated/0/Download)
fun createFile() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
// 文件类型
intent.type = "text/plain"
// 文件名称
intent.putExtra(Intent.EXTRA_TITLE, System.currentTimeMillis().toString() + ".txt")
startActivityForResult(intent, 11)
}
完全copy:https://blog.csdn.net/qq_17766199/article/details/104199446
目前没有遇到需要这么操作的场景,了解了大概,没详细