Android N 7.0中报错:android.os.FileUriExposedException的解决方法

文章目录

      • 原因
      • 解决方法
      • 解决方法2

最近在Android N 上 安装Apk时报错

AndroidJavaException: android.os.FileUriExposedException: 
file:///storage/emulated/0/Android/data/com.linxinfa.mygame/files/game_demo.apk 
exposed beyond app through Intent.getData()

原因

Android不再允许在app中把file://Uri暴露给其他app,包括但不局限于通过Intent或ClipData 等方法。
原因在于使用file://Uri会有一些风险,比如:

文件是私有的,接收file://Uri的app无法访问该文件。
在Android6.0之后引入运行时权限,如果接收file://Uri的app没有申请READ_EXTERNAL_STORAGE权限,在读取文件时会引发崩溃。

因此,google提供了FileProvider,使用它可以生成content://Uri来替代file://Uri。

解决方法

1 在AndroidManifest.xml中添加如下代码

<provider android:name="android.support.v4.content.FileProvider"
  android:authorities="app的包名.fileProvider"
  android:grantUriPermissions="true"
  android:exported="false">
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
provider>

注意:
authorities:app的包名.fileProvider
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:必须是false
resource:中的@xml/file_paths是我们接下来要添加的文件

2 在res目录下新建一个xml文件夹,并且新建一个file_paths的xml文件(如下图)
Android N 7.0中报错:android.os.FileUriExposedException的解决方法_第1张图片
3、打开file_paths.xml文件添加如下内容


<paths>
  <external-path path="Android/data/app的包名/" name="files_root" />
  <external-path path="." name="external_storage_root" />
paths>

path:需要临时授权访问的路径(.代表所有路径)
name:就是你给这个访问路径起个名字

4、修改代码适配Android N

Intent intent = new Intent(Intent.ACTION_VIEW);
//判断是否是AndroidN以及更高的版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
  Uri contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile);
  intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
  intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
startActivity(intent);

1、首先我们对Android N及以上做判断;
2、然后添加flags,表明我们要被授予什么样的临时权限
3、以前我们直接 Uri.fromFile(apkFile)构建出一个Uri,现在我们使用FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + “.fileProvider”, apkFile);
4、BuildConfig.APPLICATION_ID直接是应用的包名


解决方法2

看到别人说有更简单粗暴的办法,直接禁用新特性,如下:
在库的onCreate方法里,添加如下代码

if(Build.VERSION.SDK_INT>=24){
    try{
        Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
        m.invoke(null);
    }catch(Exception e){
        e.printStackTrace();
    }
}

经测试,在安卓7,安卓8,安卓9成功。

你可能感兴趣的:(android,java)