前言:7.0版本更新FileProvider的使用網上很多就不講了,本文主要講述這次發版遇到的一系列坑。。。
前天喜滋滋的釋出了新版本,木有想到昨天就出現問題了,那就是Android 8.0系統居然不能下載下傳安裝,或是下載下傳成功了也沒有跳出應用安裝界面。于是我不管三七二十一先百度了一波,大概意思就是Android 8.0的系統中,“未知來源應用權限”的開關被移除掉了,取而代之的是未知來源應用的管理清單,如果你想要安裝某個被自己所信任的開發者的app,則需要在每一次都手動授權“安裝未知應用”的許可。
網上的解決其實很簡單:
1.在AndroidManifest.xml檔案中,添加REQUEST_INSTALL_PACKAGES權限
2.在打開安裝包的代碼中添加“相容Android 8.0”的代碼
//相容8.0
boolean installAllowed;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
installAllowed = context.getPackageManager().canRequestPackageInstalls();
if (installAllowed) {
installApk(file);
} else {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
installApk(file);
return;
}
} else {
installApk(file);
}
//安裝apk,相容7.0
protected void installApk(File file) {
if (!file.exists()) {
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
//版本在7.0以上是不能直接通過uri通路的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//參數1 上下文, 參數2 Provider主機位址和清單檔案中保持一緻 參數3 共享的檔案
Uri apkUri =
FileProvider.getUriForFile(context, "com.xxx.fileProvider", file);
//添加這一句表示對目标應用臨時授權該Uri所代表的檔案
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
// intent.setDataAndType(Uri.parse("file://" + file.toString()), "application/vnd.android.package-archive");
// 由于沒有在Activity環境下啟動Activity,設定下面的标簽
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
在這個過程中我還遇到了一個坑,那就是canRequestPackageInstalls一直傳回false。查了資料才發現targetSdkVersion是26以上才能擷取正确的canRequestPackageInstalls,否則就一直傳回false。(ps:我之前的targetSdkVersion是24)。
做完這兩步後可以下載下傳并自動跳轉到未知來源應用權限界面,你以為這樣就完了嗎?并沒有!!!
運作了幾次後直接下載下傳進度沒有了,換言之就是下載下傳不了! what??? 一陣煩躁。。。
想不到到底是哪兒出現問題了呢,剛剛還好好的運作,咋地突然就不能下載下傳了。。。
檢視了報錯日志,如下:

報錯日志.png
意思就是使用者拒絕了權限,可是我明明一開始就動态設定權限允許了呀,為什麼還會出現這個??不信邪的我特意去看這個APP下看了權限,确實存儲權限是開了的呀,一臉懵逼。。。
第一感覺會不會又是Android 8.0的問題呢,結果确實是,是因為代碼中動态申請的其實是READ_EXTERNAL_STORAGE讀存儲權限,這在Android O(Android 8.0)之前是沒有任何問題的,因為讀寫是一組權限,同屬存儲權限,隻要申請了同組權限中的一個,同組中的其他在清單檔案中列出了的權限也就被授予了。但是Android O(Android 8.0)運作時權限有了變動,就是系統隻會授予應用明确請求的權限,
然而一旦使用者為應用授予某個權限,則所有後續對該權限組中權限的請求都将被自動準許,但是還是需要去申請,這點和Android O(Android 8.0)之前不同。
由于這裡建立下載下傳檔案,實際上是往存儲中寫檔案,需要寫存儲權限WRITE_EXTERNAL_STORAGE,于是将代碼點選更新時申請READ_EXTERNAL_STORAGE改為申請WRITE_EXTERNAL_STORAGE。運作測試,APK檔案是可以下載下傳成功了。至于之前幾次為什麼可以下載下傳,我還是想不明白,可能和targetSdkVersion有關。
如果你以為我這次的坑徹底結束了,那你就錯了!!!因為這些解決完之後結果解析包安裝失敗了,,why???
查了資料大體就是Android Studio打包問題,現在Android Studio打包出現了兩個選擇signature versions:V1(Jar Signature) and V2(Full APK Signature) 。以下是官方說法:
Android 7.0 引入一項新的應用簽名方案 APK Signature Scheme v2,它能提供更快的應用安裝時間和更多針對未授權 APK 檔案更改的保護。在預設情況下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 會使用 APK Signature Scheme v2 和傳統簽名方案來簽署應用。
這項新方案并非強制性的,如果應用在使用 APK Signature Scheme v2 時不能正确開發,可以停用這項新方案。禁用過程會導緻 Android Studio 2.2 和 Android Plugin for Gradle 2.2 僅使用傳統簽名方案來簽署應用。要僅用傳統方案簽署,打開子產品級 build.gradle 檔案,然後将行 v2SigningEnabled false 添加到版本簽名配置中:
android {
…
defaultConfig { … }
signingConfigs {
debug {
storeFile file("./xxx.keystore")
storePassword 'password'
keyAlias 'xxx'
keyPassword 'password'
}
release {
storeFile file("./xxx.keystore")
storePassword 'password'
keyAlias 'xxx'
keyPassword 'password'
v2SigningEnabled false
}
}
根據官方文檔,就是在我們的gradle檔案裡的相應位置添加這行代碼
v2SigningEnabled false
but!!!我添加了之後還是出現了解析包安裝失敗,,藍瘦香菇。。。路漫漫~~~隻能繼續摸索
當發現是 7.0 系統上才會出現的問題之後,再聯系報錯資訊,很容易就想到 FileProvider 的權限問題,然而并沒有什麼用,我還是不知道怎麼回事。對比之前實作的版本更新的代碼,定位到 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)這句代碼,因為我把intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)放在了intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)前面,當把intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)這句話放在intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)前面,就是正常的。
這是因為對 setFlags() 和 addFlags() 認知不清,錯誤使用導緻的,最後,将 setFlags()操作放在 addFlags() 之前解決了這個問題。
//安裝apk,相容7.0
protected void installApk(File file) {
if (!file.exists()) {
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
// 由于沒有在Activity環境下啟動Activity,設定下面的标簽 setFlags要放在addFlags之前
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//版本在7.0以上是不能直接通過uri通路的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//參數1 上下文, 參數2 Provider主機位址和清單檔案中保持一緻 參數3 共享的檔案
Uri apkUri =
FileProvider.getUriForFile(context, "com.xxx.fileProvider", file);
//添加這一句表示對目标應用臨時授權該Uri所代表的檔案
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
// intent.setDataAndType(Uri.parse("file://" + file.toString()), "application/vnd.android.package-archive");
context.startActivity(intent);
}
intent.setFlags() 和 intent.addFlags() 的差別
setFlags():為intent 設定特殊的标志,會覆寫 intent 已經設定的所有标志。
public Intent setFlags(int flags) {
mFlags = flags;
return this;
}
addFlags():為intent 添加特殊的标志,不會覆寫,隻會追加。
public Intent addFlags(int flags) {
mFlags |= flags;
return this;
}
之前更新Android 7.0并沒有這個問題,應該也是和targetSdkVersion有關。
到此,,此次版本更新遇到的坑總算填完了。。。
總結此次的坑:
一、Android 8.0下載下傳失敗。解決方案:把Android 6.0的動态申請權限READ_EXTERNAL_STORAGE改為申請WRITE_EXTERNAL_STORAGE。
二、Android 8.0下載下傳成功後無法跳到自動更新頁面。解決方案:授權“安裝未知應用”的許可。
三、授權“安裝未知應用”的許可的時候擷取canRequestPackageInstalls一直傳回false。解決方案:targetSdkVersion必須大于等于26(我之前是24)。
四、Android 7.0 8.0 解析包安裝失敗。解決方案:安裝時把intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)這句話放在intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)前面。
好了,,這次真的是講完了,共勉。。。