Android API≥24下FileProvider使用报错及解决方案问询
解决Android API≥24下的FileProvider和DownloadManager兼容性问题
我来帮你逐个搞定这两个头疼的兼容性问题:
一、FileProvider抛出IllegalArgumentException(找不到配置根目录)
从报错日志里能看到路径变成了/file:/storage/emulated/0/Images/IMAGE_xxx.jpg——这明显是多了个file://前缀搞出来的问题!你在调用FileProvider.getUriForFile时,错误地把file://和已有的File对象路径拼接成了新的File,可File本身就代表本地文件路径,完全不需要加这个前缀。
修正后的FileProvider代码:
if(vid.equals("")) { inFileName = img.substring(img.lastIndexOf('/') + 1); File sdcard = Environment.getExternalStorageDirectory(); File filex = new File(sdcard, inDir + "/" + "IMAGE_" + inFileName); if(filex.exists()) { if (inDialog.isShowing()) inDialog.dismiss(); // 直接传入已有的filex对象,不要画蛇添足加file://前缀 Uri uri = FileProvider.getUriForFile( inContext, inContext.getApplicationContext().getPackageName() + ".provider", filex ); Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(uri, "image/*"); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); inContext.startActivity(i); } else { inTmp = Uri.parse(img); Image_DownloadId = DownloadData(inTmp, "IMAGE_" + inFileName); } }
还要确认FileProvider的XML配置是否正确
如果还没配置,先在res/xml下创建file_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 对应外部存储根目录,匹配你代码里的Environment.getExternalStorageDirectory() --> <external-path name="external_files" path="."/> </paths>
然后在AndroidManifest.xml里注册FileProvider:
<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>
注意:如果你用的是旧版support库,把provider name改成android.support.v4.content.FileProvider即可。
二、BroadcastReceiver中DownloadManager.COLUMN_LOCAL_FILENAME弃用引发SecurityException
从Android Q(API 29)开始,直接读取COLUMN_LOCAL_FILENAME会触发权限异常,官方推荐用COLUMN_LOCAL_URI获取文件Uri,通过ContentResolver来操作,或者直接用这个Uri构建Intent。
修正后的BroadcastReceiver代码:
BroadcastReceiver downloadReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if(referenceId == Image_DownloadId) { DownloadManager.Query ImageDownloadQuery = new DownloadManager.Query(); ImageDownloadQuery.setFilterById(Image_DownloadId); Cursor cursor = downloadManager.query(ImageDownloadQuery); if(cursor.moveToFirst()){ // 替换弃用的COLUMN_LOCAL_FILENAME,改用COLUMN_LOCAL_URI获取文件Uri int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI); Uri fileUri = Uri.parse(cursor.getString(uriIndex)); Intent i = new Intent(); if(inTenTipe.equals("repost")) { i.setAction(Intent.ACTION_SEND); i.putExtra(Intent.EXTRA_STREAM, fileUri); i.setType("image/*"); i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); }else { i.setAction(Intent.ACTION_VIEW); i.setDataAndType(fileUri, "image/*"); i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } try { if (inDialog.isShowing()) inDialog.dismiss(); // 非Activity上下文启动需要加这个flag if (!(context instanceof Activity)) { i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } context.startActivity(i); } catch (android.content.ActivityNotFoundException ex) { // 根据系统版本选择文件删除方式,适配Android Q+的存储权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { context.getContentResolver().delete(fileUri, null, null); } else { File file = new File(fileUri.getPath()); boolean deleted = file.delete(); } Image_DownloadId = DownloadData(inTmp, "IMAGE_" + inFileName); } } // 别忘了关闭Cursor,避免内存泄漏 cursor.close(); } } };
关键改动说明:
- 用
COLUMN_LOCAL_URI替代COLUMN_LOCAL_FILENAME,直接获取文件Uri,绕开本地路径的权限限制; - 给Intent添加
FLAG_GRANT_READ_URI_PERMISSION,确保目标应用能读取这个Uri指向的文件; - 适配Android Q+的存储规则,删除文件时用ContentResolver而非直接操作File;
- 新增Cursor关闭逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Dany Hermawan
相关产品推荐
相关产品推荐

