Android 10 及以上版本如何读写非沙箱存储区域?

Android 10 及以上版本如何读写非沙箱存储区域?

前言

众所周知,在Android 10及以上版本,谷歌使用了fuse文件系统,针对外置存储使用了独立的沙箱存储空间,就是说,一般的应用程序只允许在/storage/emulated/${userid}/Android/${dir}/${package}/下读写文件,并且不需要权限。如果你想读写此路径以外的区域,抱歉,不被允许!

过渡

在Android 10上,谷歌给软件开发者提供的过度方式是:

  • 目标SDK降到29以下
  • 在清单文件的application节点中加入android:requestLegacyExternalStorage="false",即可按照8.0、9.0的方式去读写非包名目录下的区域

第一种方式的话就不能使用新SDK带来的各种特性,如果对项目没有什么影响,可以暂时使用这种方式,避免了各种配置;

第二种方式是官方提供的过渡方式,在Android 11 之前可以用,到了Android 11之后就失效了,必须适配存储模型。

适配

步骤一

申请读写权限:

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

步骤二

在Android 10上,我们直接忽略这个存储模型即可:

    <application
        ...
        android:requestLegacyExternalStorage="false"
	...
	>

	...

    </application>

上面的声明保证安卓10以下,都只需动态声明读写权限即可。

步骤四

动态申请权限,为了方便,使用的是rxPermissions框架:

//6.0以上动态请求权限
        val perResult = rxPermissions.request(
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE
        ).subscribe {
            if (it) {
                //权限完全通过
                Log.e("Main", "权限获取成功")
            } else {
                showDefaultMsg("有权限未被允许")
            }
        }

到这里,已经基本适配到了Android 10了,但Android 11需要另外适配

步骤五

在Android 11上,多了一个权限设置——允许读写外部访问存储,这个权限请求到时候,会跳到设置页面,引导用户开启:

	//务必要判断是否是Android 11,因为Android 10及以下是没有isExternalStorageManager方法的。
	if (Build.VERSION.SDK_INT >= 30){
		//判断是否已经拥有外部存储读写权限了
                if (Environment.isExternalStorageManager()) {
                    //已经有读写权限了
                    loge("已经有读写权限了")
		    //开始你的读写业务吧
                    rw(false,"OKOK Android 11")
                } else {
                    loge("没有读写权限")
                    //跳转到设置页面,去引导用户开启
                    val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
                    startActivity(intent)
                }
            }else{
		//已经有读写权限,开始读写业务
                rw(false,"OKOK Android 11")
            }

到此,适配完毕。

结束

之前一直担心如果高版本到安卓系统不能读写外部存储设备,那将会限制很多功能,仔细一想应该也不会,因为很多第三方浏览器、病毒查杀软件等等,都需要读写外部存储设备,没理由一刀切的。