Android7.0系统中在app内部安装apk时失败或者报错FileUriExposedException

最近遇到了这个问题,在调试和查找资料的时候,发现了很多问题,网上的解决方案也有的有问题,于是自己写一个记下!
首先,出现这个问题的原因:从Android 7.0开始,不再允许在app中把file:// Uri暴露给其他app,否则应用会抛出FileUriExposedException。原因在于,Google认为使用file:// Uri存在一定的风险。比如,文件是私有的,其他app无法访问该文件,或者其他app没有申请READ_EXTERNAL_STORAGE运行时权限。解决方案是,使用FileProvider生成content:// Uri来替代file:// Uri。
另外,并不是所有的手机都会因为这个异常崩溃,比如华为的不会崩溃,而小米6会,其他机型没有测试过。

解决方案:
在manifests.xml清单文件中添加:


...
"android.support.v4.content.FileProvider"
    android:authorities="com.xxxxx.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    "android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_path"/>

其中,authorities属性可自定义,是provider的唯一标识,一般用自己项目的包名,以保证唯一性。exported必须设置成 false,否则运行时会报错java.lang.SecurityException: Provider must not be exported 。grantUriPermissions用来控制共享文件的访问权限。

meta-data节点中的android:resource指定了共享文件的路径。此处的file_paths即是该Provider对外提供文件的目录的配置文件,存放在res/xml/下。内容如下:


<paths>
    <external-path
        name="rc_external_path"
        path="."/>
paths>


其中根元素<paths>是固定的,内部元素可以是以下节点:
<files-path name="name" path="path" /> 对应getFilesDir()。
<cache-path name="name" path="path" /> 对应getCacheDir()。
<external-path name="name" path="path" /> 对应Environment.getExternalStorageDirectory()。
<external-files-path name="name" path="path" /> 对应getExternalFilesDir()。
<external-cache-path name="name" path="path" /> 对应getExternalCacheDir()。
“.”代表全路径

注意:有些第三方的module(比如融云即时通讯)会有些需要在你项目的主module清单文件中添加这个,这时候就不需要再添加了,否则会编译不通过,然后只需要在已存在的provider定义好的resource文件中添加就可以了。

之后就是如何使用了:

public void installApk(File file) {
    if (file.exists()) {
        Intent intent = new Intent();
        Uri data;
        // 判断版本大于等于7.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setAction(Intent.ACTION_INSTALL_PACKAGE);
            //清单文件中配置的authorities
            data = FileProvider.getUriForFile(this, "com.snyj.wsd.kangaibang.FileProvider", file);
            // 给目标应用一个临时授权
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//重点!!
        } else {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//重点!!!
            data = Uri.fromFile(file);
        }
        intent.setDataAndType(data, "application/vnd.android.package-archive");
        startActivity(intent);
    } else {
        Log.e("ruin", file.getName()+"文件不存在!" );
    }
}

这里面标重点的一定要注意!版本大于7.0和小于7.0的setFlags是不一样的!!

你可能感兴趣的:(系统版本问题)