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"/>
複製程式碼
然後在程式碼中請求下許可權即可。