安卓11及以上版本读取本地文件的方法

Posted 问就是没掉发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓11及以上版本读取本地文件的方法相关的知识,希望对你有一定的参考价值。

文章目录


前言

在安卓10.0及以上,系统新增了沙盒模式,进一步增强了用户的隐私性以及开发的规范性。但是在10.0时,我们可以androidManifest.xml中添加 android:requestLegacyExternalStorage=“true” 的方式,把沙盒模式关闭继续使用9.0及以下的方式去读取本地文件。但是在11.0开始,该方式将不在起作用,我们就需要去适配沙盒模式。
本篇博客借鉴了郭霖大神的博客。郭霖YYDS


一、沙盒模式

什么是沙盒模式?详细可见官方文档数据和文件存储概览
我从文档中总结了关于APP可使用的存储方式,包括data/data 内部存储目录、缓存目录、应用外部专属目录、Download共享目录等。
在我尝试去使用外部的Download目录时我发现一个问题:既然文档上说可以通过Downlaod共享目录去读取外部文件,那么是不是说任意的外部文件只要我放在Download中我都能读取呢? 结果很明显,我失败了(只能拿到应用本身创建、系统文件例如照片、视频等,其他APP产生的文件或外部手动导入的文件均拿不到那么11.0以上我们怎么才能获取到本地想要的文件呢? 往下看。

二、开始黑科技

1.权限申请

代码如下(示例):

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

前两个读写权限就不说了,重点戏是第三个MANAGE_EXTERNAL_STORAGE 管理外部存储权限。
该权限属于特殊权限需要用户手动去设置中打开,代码如下。

 private fun checkStorageManagerPermission() 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R ||
            Environment.isExternalStorageManager()
        ) 
            Toast.makeText(this, "已获得访问所有文件权限", Toast.LENGTH_SHORT).show()
         else 
            val builder = AlertDialog.Builder(this)
                .setMessage("本程序需要您同意允许访问所有文件权限")
                .setPositiveButton("确定")  _, _ ->
                    val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
                    startActivity(intent)
                
            builder.show()
        
    

代码中用 Environment.isExternalStorageManager() 来判断APP是否拥有该权限,若无此权限,则将页面手动跳转至设置页面,让用户手动打开该权限,如下图。



需要用户手动“授予所有文件的管理权限”打开,下面我么开始测试能否读取任意文件,并想9.0及以下用文件的绝对路径去操作该文件。

2.开始操作

代码如下(示例):

        val rootPath =
            getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath
        Log.e(">>>", rootPath)
        val listFiles = File(rootPath).listFiles()
        listFiles?.forEach 
            Log.e(">>>", it.name)
            if (it.name == "test") 
                Log.e(">>>", it.name)
                it.listFiles()?.forEach  file ->
                    Log.e(">>>", file.name)
                    if (file.name == "1.jpg") 
                        startActivity(MainActivity2.getActivity(this, file.absolutePath))
                        finish()
                    
                
            

我们在请求权限的if判断后,添加这些代码。去获取Download共享目录下所有的文件,并手动创建一个test的文件夹,并在其中还防止一张名为1.jpg的图片供MainActivity2放问并展示,如下图。

接下来我们执行代码,看看日志有没有把所有Download目录下所有文件打印出来。

可以看见Download目录下的所有文件我们都成功获取到了,并且也找到了test文件夹以及其中的1.jpg,如下图。说明APP拥有该权限后,沙盒模式将不在阻拦我们拿取任意文件。


总结

经过上面的一些简单测试,我们不难发现当我们申请 MANAGE_EXTERNAL_STORAGE 管理外部存储权限 后,完全可以当做之前外部存储的方式去读取,完美解决沙盒模式对拿取外部的非APP创建的文件的困扰。
PS.若小伙伴们需要把APP发布到谷歌商店中,慎重使用该权限,因为该权限本为浏览器或本地文件操作器类型的APP设计,所以一般APP申请该权限,谷歌可能不会同意。
最后文章或代码中若有问题,欢迎大家指正或在评论下一起探讨沙盒模式的相关问题。

安卓7.0及以上版本抓包https失败解决方法

现象

android7.0以上的手机https抓包失败(安装了https证书也不行)

原因

android7.0+的版本新增了证书验证(系统证书)

解决办法

前提:在手机端和电脑端都必须安装https的安全证书
配置:打测试包时,项目设置默认信任所有证书(系统+用户)
1.在工程res-xml目录中创建一个名为 network_security_config.xml的文件,文件内容如下:

<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" overridePins="true" />
            <certificates src="user" overridePins="true" />
        </trust-anchors>
    </base-config>
</network-security-config>

2.在AndroidManifest里的标签中,添加如下代码:

android:networkSecurityConfig="@xml/network_security_config"

重新打包项目,然后抓包,即可成功。

webview抓包失败

上面可以解决android原生抓包问题,但在android7.0以上的手机,开着网络代理访问不了webview,若要抓包webview,
需要在webview的WebViewClient中,将一行代码给注释掉:

super.onReceivedSslError(view, handler, error)

这样是为了忽略掉SSL证书错误,因为开启代理后网络会变得不安全,证书会报错误,webview检测到证书错误之后就不请求任何数据。 注释是为了忽略掉父类的处理,默认执行下去。

警告

这样的配置操作是敏感且危险的,只能用于测试环境方便抓包,线上包一定注意要恢复配置。
后记
通过查找资料,还有一个方法,通过重载WebViewClient的onReceivedSslError()函数并在其中执行handler.proceed(),即可忽略SSL证书错误,继续加载页面,代码如下:

WebView webview = (WebView) findViewById(R.id.webview);
webview.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        // 不要调用super.onReceivedSslError,因为其包含了一条 handler.cancel(),第一次访问时无法加载,第二次以后可以加载
        // super.onReceivedSslError(view, handler, error);
        // 忽略SSL证书错误,继续加载页面
        handler.proceed();
    }
}





以上是关于安卓11及以上版本读取本地文件的方法的主要内容,如果未能解决你的问题,请参考以下文章

安卓7.0及以上版本抓包https失败解决方法

如何在 Android Q 及以上版本中保存/读取文件(即 txt、mht、pdf 等),因为 getExternalStoragePublicDirectory() 已弃用 [重复]

处理Android SDK 29及以上版本,读取不到/storage/emulated/0/(/storage/sdcard/)路径下文件问题

安卓7.0及以上系统如何抓取Https的包

unity发布安卓怎么读取本地文件

Android开发过程中的坑及解决方法收录