Android Q访问公共外部存储受限

本文详细解析了从AndroidQ开始的应用外部存储访问变化,包括访问限制、异常现象及解决方案,特别是针对私有目录访问无需读写权限的情况,以及如何避免访问非私有目录时的FileNotFoundException异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

从Android Q(即 Android 10)开始,应用访问外部存储的私有目录(即Context.getExternalFilesDir())不需要申请READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE权限。同时,正常情况下,就算应用有申请READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE权限,也只能访问外部存储的私有目录,若是访问了除了私有目录之外的其他外部储存,会抛出FileNotFoundException异常

二、异常现象

1、若是你的应用设置 targetSdkVersion = 29,在android Q的手机访问非私有目录的外部储存时,即使是有读写外存的权限,依然会抛出了类似下面的FileNotFoundException异常:

java.io.FileNotFoundException: /storage/emulated/0/min77/51850812e89ff8ead2002c15ebf417ac: open failed: EACCES (Permission denied)
    at libcore.io.IoBridge.open(IoBridge.java:496)
    at java.io.FileInputStream.<init>(FileInputStream.java:159)
    at com.sswl.sdk.utils.Logger.readFileStream(Logger.java:107)
    at com.sswl.sdk.utils.Logger.readProterties(Logger.java:82)
    at com.sswl.sdk.utils.Logger.loadLogConfig(Logger.java:26)
    at com.sswl.sdk.utils.Logger.w(Logger.java:67)
    at com.sswl.sdk.network.http.NetworkRequest.doPostRequest(NetworkRequest.java:68)
    at com.sswl.sdk.network.http.NetworkRequest.doPostRequest(NetworkRequest.java:47)
    at com.sswl.sdk.network.http.NetworkRequest.doRequest(NetworkRequest.java:35)
    at com.sswl.sdk.network.http.NetworkRequest.doRequest(NetworkRequest.java:28)
    at com.sswl.sdk.network.http.HttpRequestAsyncTask.doInBackground(HttpRequestAsyncTask.java:65)
    at com.sswl.sdk.network.http.HttpRequestAsyncTask.doInBackground(HttpRequestAsyncTask.java:21)
    at android.os.AsyncTask$3.call(AsyncTask.java:378)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:919)
Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
    at libcore.io.Linux.open(Native Method)
    at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
    at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
    at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7419)
    at libcore.io.IoBridge.open(IoBridge.java:482)
	... 16 more

三、异常分析

1、Android Q只允许应用读取自己外部储存的私有目录,这个不需要读写外存权限;
2、当应用被授予读写外存的权限之后,也只能读取以下目录的文件:存储在MediaStore.Images的图片存储在MediaStore.Video的视频文件存储在MediaStore.Audio的音频文件
在这里插入图片描述
3、要是想要读取上述之外的外部存储文件,就会抛出FileNotFoundException异常

四、解决方案

1、方案一:把应用的 targetSdkVersion设置为29以下即可
2、方案二:在AndroidManifest.xml的Application节点设置android:requestLegacyExternalStorage=“true”

 <!-- This attribute is "false" by default on apps targeting
       Android 10 or higher. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>

五、延伸问题

1、要是使用方案二来解决上述问题,正常出包是没问题的,但是要是使用apktool 反编译回编译,就会出现找不到android:requestLegacyExternalStorage属性异常,目前apktool最新版本是2.4.0,估计后续的版本会兼容Android Q
2、解决方案:
1)先在Android Studio中新建一个API 29的模拟器
在这里插入图片描述
2)运行创建好的模拟器
在这里插入图片描述
3)使用命令adb pull 从模拟器的system/framework 提取出framework-res.apk到电脑本地

adb pull system/framework/framework-res.apk C:\Users\Administrator.USER-20190314H
O\Desktop\1

4)将从模拟器中拉取出来的framework-res.apk改名为1.apk,然后拷贝到:C:\Users\当前用户名\AppData\Local\apktool\framework目录下,然后重新用apktool回编译即可
【备注】:若是有androidQ的手机,也可以直接从手机上拉取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值