记录android11文件管理适配

4,782 阅读3分钟

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
目前没有遇到需要这么操作的场景,了解了大概,没详细