安卓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申请该权限,谷歌可能不会同意。
最后文章或代码中若有问题,欢迎大家指正或在评论下一起探讨沙盒模式的相关问题。

  • 17
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android应用开发中,如果需要读取本地文件,可以通过JNI(Java Native Interface)来实现。 JNI是用于在Java代码与本地代码(C/C++)之间进行交互的一种技术。通过JNI,我们可以在Java代码中调用本地代码来访问系统底层资源,例如读取本地文件。 以下是一种实现方式: 1. 创建一个Java类,用于加载本地库文件和声明本地方法。例如: ``` public class NativeFileReader { static { System.loadLibrary("native-library"); } public static native String readFile(String filePath); } ``` 2. 在本地代码中实现读取文件方法,并将内容转换为字符串返回。例如: ``` #include <jni.h> #include <stdio.h> JNIEXPORT jstring JNICALL Java_com_example_NativeFileReader_readFile(JNIEnv* env, jclass clazz, jstring filePath) { const char* filePathStr = (*env)->GetStringUTFChars(env, filePath, NULL); FILE* file = fopen(filePathStr, "r"); if (file == NULL) { return NULL; } fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); char* buffer = malloc(fileSize + 1); fread(buffer, 1, fileSize, file); buffer[fileSize] = '\0'; fclose(file); (*env)->ReleaseStringUTFChars(env, filePath, filePathStr); jstring result = (*env)->NewStringUTF(env, buffer); free(buffer); return result; } ``` 3. 将本地代码编译为动态库文件,例如libnative-library.so。 4. 将生成的动态库文件放置到项目的jniLibs目录中。 5. 在Java代码中调用NativeFileReader类的readFile方法,传入要读取文件路径,就可以得到读取到的文件内容。例如: ``` String filePath = "/sdcard/example.txt"; String fileContent = NativeFileReader.readFile(filePath); ``` 这样,就能够通过JNI来读取本地文件了。需要注意的是,为了保证文件读取的权限,需要在AndroidManifest.xml文件中添加文件读写权限的声明。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值