Android7.0安装APK报错FileUriExposedException
@Author GQ 2018年04月09日
targetVersion写到了25但代码一直没适配7.0导致安装APK时报错记录一下解决过程
报错日志
由于使用了第三方下载库(https://github.com/yaming116/UpdateApp):
Caused by: android.os.FileUriExposedException: file:///storage/emulated/0/OA/downLoad/updateAPP_1.0.apk exposed beyond app through Intent.getData()
OA/downLoad/
是自己app创建的文件夹, 注意下载到自定义文件夹需要requestpermiss用户主动授权读写权限
配置
在 AndroidManifest.xml
的 Application
中
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities"> //replace会导致下面原因第2点,一般情况不用这行
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"
tools:replace="android:resource" />
</provider>
- 注意在步骤1中
authorities
是应用包名.fileProvider 或者也可以直接写成自己报名com.xx.xx.fileProvider
在 res
资源文件下新建目录xml
,在 xml
目录下新建 file_paths.xml
文件
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--"."代表全部路径-->
<external-path
name="external_files"
path="." />
</paths>
安装APK代码
public static void installApk(Context context, File file) {
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context,
context.getApplicationContext().getPackageName() + ".provider",
file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
uri = Uri.fromFile(file);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
}
context.startActivity(intent);
}
分析原因
1.打断点看到在下载执行过程中,直接到error()失败方法,应该是源码的某些地方配置导致失败,所以直接把源码拷到本地,准备修改一下;
2.可能由于本地项目使用了多个依赖工程, 比如图片选择器等也使用到
fileProvider
导致authorities
冲突会报错如下:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:560)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:534)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:376)
解决办法
将源码拷贝到本地,看一下是源码的什么位置导致错误, 发现 在
getFileProviderAuthority()
方法中try { for (ProviderInfo provider : context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS).providers) { if (FileProvider.class.getName().equals(provider.name) && provider.authority.endsWith(".update_app.file_provider")) { return provider.authority; } } } catch (PackageManager.NameNotFoundException ignore) { } return null;
- 由于我们的包名中没有以这个
.update_app.file_provider
结尾所以返回null
导致回调error()
方法
所以改造一下
installIntent
方法private static Intent installIntent(Context context, String path) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory(Intent.CATEGORY_DEFAULT); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Uri fileUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", new File(path));//这里不用作者的getFileProviderAuthority()方法,直接写成BuildConfig.APPLICATION_ID或者写死成"com.xx.xx"自己的包名就好了 intent.setDataAndType(fileUri, "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { intent.setDataAndType(Uri.fromFile(new File(path)), "application/vnd.android.package-archive"); } return intent; }
- 由于我们的包名中没有以这个
暂时将依赖文件直接写到主工程中, 用同一份
<provider/>
避免使用tools:replace="android:authorities"
导致包名不一致报错 ,不知道大神们有没有更好的解决办法!