1.为什么会分为6.0、7.0、8.0 三个版本呢?

(1)6.0以及之前算是一个版本的问题,7.0版本添加了提高了私有文件的安全性FileProvider是一个坑,8.0版本对于Install Apk又增加了
权限管理又是一个坑

那么算下来就有两个坑了,我们一一看看我的坑是怎么样的。
复制代码

2.android 7.0的坑

(1)这个坑是一个主要的坑,我相信很多的anroid开发者都应该知道Android 7.0 添加私有文件的安全性,那么来看下我的代码:
复制代码
/**
     * 通过隐式意图调用系统安装程序安装APK
     */
    public static void install(Context context) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        // 由于没有在Activity环境下启动Activity,设置下面的标签
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(Uri.fromFile(
                new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "myApp.apk")),
                "application/vnd.android.package-archive");
        context.startActivity(intent);
    }
复制代码
相信这段代码是我们很多6.0及以前的通过Intent隐式安装APK的方法,当然这个方法在6.0以及前是么有任何问题的,但是在7.0这里就会出现
问题了
复制代码



这就表示我们用绝对路径去访问文件的时候就会出现SecurityException的异常。
大神可以直接移步官网介绍: https://developer.android.google.cn/training/secure-file-sharing/index.html

## (2)具体采坑的内容:

一、在AndroidManifest.xml里声明Provider
复制代码
<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.test.pr.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
</provider>
复制代码
name: android V4 包中的类FileProvider
authorities:你的文件的Uri的域名  一般以包名.fileprovider的格式,防止重名
exported: 设置不允许导出,我们的FileProvider应该是私有的
grantUriPermissions:允许获取文件的临时访问权限
resourse: 设置FileProvider访问的文件路径

二、配置FileProvider的文件共享路径

首先我们在res文件下面创建一个xml文件夹,然后再xml中创建一个manifest中声明的文件file_paths.xml。这个名字看你自己定义的,但是必须和AndroidManifest.xml中声明的一致:
复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path
            name="Download"
            path="test"/>
    </paths>
    .
    .
    .
</resources>
复制代码
这里可以创建很多个paths,但是每个paths的name不能一样。
name:FileProvider 提供的Uri中一个必要的,这里的name=Download,那么获得Uri的连接如下所示:
复制代码
content://com.test.pr.fileprovider/Download
复制代码
接下来external-path这个标签是最坑的地方,我来慢慢解释,我当时出问题就是在这个问题:

     <files-path name="*name*" path="*path*" />   对应的是:Context.getFileDir()的路径地址
     以上面的配置的path="test"来说
     得到路径:Context.getFileDir()+"/test/"


     <cache-path name="*name*" path="*path*" />   对应路径:Context.getCacheFir()
     以上面的配置的path="test"来说
     得到路径:Context.getCache()+"/test/"


     <external-path name="*name*" path="*path*" />   对应路径:Environment.getExternalStorageDirectory()
     以上面的配置的path="test"来说
     得到路径:Environment.getExternalStorageDirectory()+"/test/"


     <external-files-path name="*name*" path="*path*" />   对应路径:Context.getExternalStorageDirectory()
     以上面的配置的path="test"来说
     得到路径:Context.getExternalStorageDirectory()+"/test/"


     <external-cache-path name="*name*" path="*path*" />   对应路径: Context.getExternalCacheDir()
     以上面的配置的path="test"来说
     得到路径:Context.getExternalCacheDir()+"/test/"
复制代码

PS:我再啰嗦几句:上面的这个path标签一定要和你保存的文件的位置要对应起来,如果文件的路径和你标签对应的位置有错的话,就会报这种错误:

错误: failed to find configured root that contains。。。
复制代码

我再把我的Install APk的代码如下:

public class InstallUtils {
    /**
     * 安装apk
     *
     * @param downLoadId
     */
    public static void install(long downLoadId, Context mContext) {
        DownloadManager downloader = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
        assert downloader != null;
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), AppConstants.APP_NAME);
        if (!file.exists()) {
            ToastUtils.showToast(mContext, mContext.getString(R.string.install_fail));
            return;
        }
        Logger.e("" + file.length());
        //安装
        Intent install = new Intent(Intent.ACTION_VIEW);
        //判断是否是android 7.0及以上
        if (Build.VERSION.SDK_INT >= AppConstants.ANDROID_VERSION_7) {
            //7.0获取存储文件的uri
            Uri uri = FileProvider.getUriForFile(mContext, "com.zhiyunqiao.pr.fileprovider", file);
            install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //赋予临时权限
            install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //设置dataAndType
            install.setDataAndType(uri, "application/vnd.android.package-archive");
        } else {
            if (file.exists()) {
                install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            } else {
                ToastUtils.showToast(mContext, mContext.getString(R.string.install_fail));
                //清除下载成功
                PreferenceHelp.remove(mContext, "Version", "update");
            }
        }
        mContext.startActivity(install);
    }

}
复制代码

3.android 8.0 主要是一个权限问题

Android 8.0更新主要是对Intent隐式安装APK做了个安全管理:
需要在mainfist中添加一行权限:
<!--8.0安装apk需要权限-->
复制代码
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
复制代码

然后在代码中请求下权限即可。