Android7.0安装APK报错FileUriExposedException

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.xmlApplication

<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" 导致包名不一致报错 ,不知道大神们有没有更好的解决办法!

猜你喜欢

转载自blog.csdn.net/baidu_25797177/article/details/79861640