Android 7.0 安装apk 广播失效问题 通过FileProvider在应用间共享文件 配置根目录

详细使用说明请参考hongyang的blog

http://blog.csdn.net/lmj623565791/article/details/72859156 

我这里主要讲安装应用内部路径apk和外部存储的apk的失败原因和解决办法,这里面借了一下别人的代码讲述一下我采的坑!!!

如果我们使用Android 7.0或者以上的原生系统,再次运行一下,你会发现应用直接停止运行,抛出了android.os.FileUriExposedException

Caused by: android.os.FileUriExposedException: 
    file:///storage/emulated/0/20170601-030254.png 
        exposed beyond app through ClipData.Item.getUri()
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1932)
    at android.net.Uri.checkFileUriExposed(Uri.java:2348)


此文主要讲一下,关于使用fileprovider的采坑过程。

(1)AndroidManifest中声明provider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.xxx.xxxx.fileprovider" 注意这个参数要和代码中启动的authorities保持一致
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
provider>

为什么要声明呢?因为FileProvider是ContentProvider子类哇~~

注意一点,他需要设置一个meta-data,里面指向一个xml文件。

注意:

  • exported:要求必须为false,为true则会报安全异常。
  • grantUriPermissions:true,表示授予 URI 临时访问权限。
  • authorities 组件标识,按照江湖规矩,都以包名开头,避免和其它应用发生冲突。
  • 上面配置文件中 android:resource="@xml/file_paths" 指的是当前组件引用 res/xml/file_paths.xml 这个文件。

(2)file_paths.xml


<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path name="root" path="" />
    <files-path name="files" path="" />
    <cache-path name="cache" path="" />
    <external-path name="external" path="" />
    <external-files-path name="name" path="path" />
     <external-cache-path name="name" path="path" />
paths>

为什么hongyang不写成这样? 真是让人迷惑


<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path name="root" path="path" />
    <files-path name="files" path="path" />
    <cache-path name="cache" path="path" />
    <external-path name="external" path="" />
    <external-files-path name="name" path="path" />
     <external-cache-path name="name" path="path" />
paths>

在paths节点内部支持以下几个子节点,分别为:

  •  代表设备的根目录new File("/");
  •  代表context.getFilesDir()
  •  代表context.getCacheDir()
  •  代表Environment.getExternalStorageDirectory()
  • 代表context.getExternalFilesDirs()
  • 代表getExternalCacheDirs()

每个节点都支持两个属性:

的时候代码没问题,所有手机运行都没问题


<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="internal_files" path="." />
    <external-files-path name="external_files" path="." />
paths>

路径内部apkFile = context.getExternalFilesDirs()+"/"xx.apk; 所有版本手机安装成功


我这里要将服务器中的apk下载到应用user/data的私有空间和下载到外部存储两种,我的配置文件如下


<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="internal_files" path="download" />
    <external-files-path name="external_files" path="download" />
paths>
/**
      * 安装 apk 文件
      *
      * @param apkFile
      */
     public void installApk(File apkFile) {
       Intent installApkIntent = new Intent();
       installApkIntent.setAction(Intent.ACTION_VIEW);
       installApkIntent.addCategory(Intent.CATEGORY_DEFAULT);
       installApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
       if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
         installApkIntent.setDataAndType(FileProvider.getUriForFile(getApplicationContext(),"com.xxx.xxxx.fileprovider", apkFile), "application/vnd.android.package-archive");
         installApkIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
       } else {
         installApkIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
       }
 
       if (getPackageManager().queryIntentActivities(installApkIntent, 0).size() > 0) {
         startActivity(installApkIntent);
       }
     }

apkFile = context.getFilesDir()+"/download"+xx.apk; 下载到内部存储的时候发现了新问题,原来在android6.0以下安装好使的代码,不好使了。原因是找不到安装文件。

apkFile = context.getExternalFilesDirs()+"/download/"+xx.apk; 下载到外部存储的时候代码没问题,所有手机运行都没问题。

于是修改配置文件和下载路径


<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="internal_files" path="." />
    <external-files-path name="external_files" path="." />
paths>

路径内部apkFile = context.getExternalFilesDirs()+"/"xx.apk; 所有版本手机安装成功

外部apkFile = context.getFilesDir()+"/"+xx.apk; 所有版本手机安装成功

申请临时的读写权限

生成 Content URI 对象后,需要对其授权访问权限。授权方式有两种: 
第一种方式,使用 Context 提供的 grantUriPermission(package, Uri, mode_flags) 方法向其他应用授权访问 URI 对象。三个参数分别表示授权访问 URI 对象的其他应用包名,授权访问的 Uri 对象,和授权类型。其中,授权类型为 Intent 类提供的读写类型常量:

FLAG_GRANT_READ_URI_PERMISSION

FLAG_GRANT_WRITE_URI_PERMISSION

或者二者同时授权。这种形式的授权方式,权限有效期截止至发生设备重启或者手动调用 revokeUriPermission() 方法撤销授权时。

第二种方式,配合 Intent 使用。通过 setData() 方法向 intent 对象添加 Content URI。然后使用 setFlags() 或者 addFlags() 方法设置读写权限,可选常量值同上。这种形式的授权方式,权限有效期截止至其它应用所处的堆栈销毁,并且一旦授权给某一个组件后,该应用的其它组件拥有相同的访问权限。

发送 Content URI

拥有授予权限的 Content URI 后,便可以通过 startActivity() 或者 setResult() 方法启动其他应用并传递授权过的 Content URI 数据。当然,也有其他方式提供服务。

如果你需要一次性传递多个 URI 对象,可以使用 intent 对象提供的 setClipData() 方法,并且 setFlags() 方法设置的权限适用于所有 Content URIs。

下面的写法是粘贴的老外一篇文章里的,应该是斜杠和点的作用是一样的。
     name="cache" path="/" />
     name=”files” path=”/” />


总结:就是在installApkIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");

startActivity(installApkIntent);

android 6.0以下的手机,安装应用user/data下面的apk时,只支持目录为context.getFilesDir()的层级,再深一层,系统都找不到这个下载后的文件安装包了。可能是6.0以下的默认权限问题。





你可能感兴趣的:(android)