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"/>
复制代码
然后在代码中请求下权限即可。