小米Android设备下载PDF时随机触发SecurityException问题求助
解决小米Android 6/7/8设备DownloadManager抛出SecurityException的问题
根据你遇到的情况——在小米Android 6、7、8设备上,明明已经授予存储运行时权限,却还是随机抛出SecurityException,错误指向Unsupported path /storage/emulated/999/Download/Somefile.pdf——我来帮你拆解问题并给出针对性解决方案:
问题根源分析
首先注意到路径里的999,这是小米设备工作资料/多用户模式的存储分区标识。在这些版本的MIUI系统中,问题主要来自三点:
- 当用户开启工作资料时,公共Download目录会切换到
/storage/emulated/999/Download,但你的应用没有获取到访问该分区的特殊权限; - 当前
FileProvider的路径配置未覆盖公共Download目录,导致DownloadManager尝试写入该路径时无法获得合法的Uri授权; - MIUI对存储权限的管控比原生Android更严格,即使申请了
WRITE_EXTERNAL_STORAGE,也可能限制跨用户分区的访问。
具体解决方案
1. 适配多用户存储路径,避免硬编码
不要直接指定下载路径,而是通过系统API获取当前用户的合法公共Download目录,自动适配普通用户和工作资料模式:
File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); File targetFile = new File(downloadDir, "Somefile.pdf");
2. 更新FileProvider的路径配置
在file_paths.xml中添加公共Download目录的映射,让FileProvider能正确授权访问该路径:
<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="images" path="images/" /> <external-files-path name="share" path="share/" /> <external-files-path name="reminder_images" path="reminder_images/" /> <!-- 添加公共Download目录的映射 --> <external-path name="public_download" path="Download/" /> </paths>
注:如果应用目标SDK是29+,可以使用<external-public-path>替代<external-path>,更贴合公共存储的访问规则。
3. 给DownloadManager正确授予Uri权限
在调用DownloadManager.enqueue()前,确保给目标Uri授予临时写入权限,避免系统权限拦截:
File targetFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "Somefile.pdf"); Uri targetUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", targetFile); // 授予DownloadManager临时写入权限 context.grantUriPermission( "com.android.providers.downloads", targetUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION ); DownloadManager.Request request = new DownloadManager.Request(Uri.parse(yourServerPdfUrl)); request.setDestinationUri(targetUri); DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); downloadManager.enqueue(request);
4. 备选方案:使用应用私有外部存储
如果上述方案仍有问题,可以考虑将文件下载到应用私有外部存储目录,这里不需要额外的运行时权限(Android 4.4+),且不受多用户模式影响:
// 获取应用私有Download目录 File privateDownloadDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), ""); if (!privateDownloadDir.exists()) { privateDownloadDir.mkdirs(); } File targetFile = new File(privateDownloadDir, "Somefile.pdf"); // 后续使用该文件路径创建Uri并发起下载
附用户提供的原始配置与堆栈信息
file_path.xml
<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="images" path="images/" /> <external-files-path name="share" path="share/" /> <external-files-path name="reminder_images" path="reminder_images/" /> </paths>
AndroidManifest
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
堆栈信息
Caused by java.lang.SecurityException: Unsupported path /storage/emulated/999/Download/Somefile.pdf at android.os.Parcel.readException(Parcel.java:2013) at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183) at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135) at android.content.ContentProviderProxy.insert(ContentProviderProxy.java:476) at android.content.ContentResolver.insert(ContentResolver.java:1555) at android.app.DownloadManager.enqueue(DownloadManager.java:1163) at com.esafirm.rxdownloader.RxDownloader.download(RxDownloader.java:83) at com.esafirm.rxdownloader.RxDownloader.download(RxDownloader.java:60) at in.okcredit.app.ui.account.AccountActivity.onReportGenerated(AccountActivity.java:320) at in.okcredit.app.ui.account.AccountPresenter$1.onSuccess(AccountPresenter.java:118) at in.okcredit.app.ui.account.AccountPresenter$1.onSuccess(AccountPresenter.java:111) at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run(SingleObserveOn.java:81) at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:119) at android.os.Handler.handleCallback(Handler.java:794) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:6651) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:824)
内容的提问来源于stack exchange,提问作者karthik kolanji
相关产品推荐
相关产品推荐

