北京時(shí)間2019年3月14日Google正式對(duì)外發(fā)布Android Q Beta 1及預(yù)覽版SDK,這意味著安卓開(kāi)發(fā)者們又即將迎來(lái)一年一度的新版本適配工作了像棘。Android Q 為開(kāi)發(fā)者們帶來(lái)了許多新功能稽亏,如折疊屏增強(qiáng)項(xiàng)、新網(wǎng)絡(luò)連接 API缕题、全新的媒體解碼器截歉、攝像頭新功能、NNAPI 擴(kuò)展烟零、Vulkan 1.1 圖形支持等等瘪松。2019/4 Beta2版本發(fā)布 咸作,5月份將會(huì)正式發(fā)布,本文將帶大家對(duì)Android Q變更和新特性的詳細(xì)解讀宵睦。
還有一點(diǎn)值得Android開(kāi)發(fā)者注意的事件记罚,華為應(yīng)用市場(chǎng)在3.26日發(fā)布了一則公告,要求華為市場(chǎng)上的App在2019年5月底前完成Android Q版本適配工作并自檢通過(guò)壳嚎,針對(duì)未適配或在Android Q版本體驗(yàn)欠佳的應(yīng)用桐智,華為應(yīng)用市場(chǎng)將在Android Q版本機(jī)型上采取下架、不推薦更新或屏蔽策略烟馅,這一點(diǎn)還需要所有開(kāi)發(fā)者注意了说庭。
同時(shí)華為4.2號(hào)的公告也需要開(kāi)發(fā)者注意了:
自2019年5月1日起,華為應(yīng)用市場(chǎng)新上架應(yīng)用應(yīng)基于Android 8.0 (API等級(jí)26郑趁,即targetSdkVersion大于等于26)及以上開(kāi)發(fā)刊驴。自2019年8月1日起,現(xiàn)有應(yīng)用的更新應(yīng)基于Android 8.0 (API等級(jí)26寡润,即targetSdkVersion大于等于26)及以上開(kāi)發(fā)捆憎。
2019年5月1日后,未達(dá)到要求的新應(yīng)用梭纹,華為應(yīng)用市場(chǎng)將拒絕上架攻礼。2019年8月1日后,未達(dá)到要求的現(xiàn)有應(yīng)用栗柒,華為應(yīng)用市場(chǎng)將拒絕更新。如您的應(yīng)用API等級(jí)小于26知举,請(qǐng)盡快完成應(yīng)用的升級(jí)改造瞬沦。
本文將從三個(gè)角度介紹Android Q的重要部分的適配問(wèn)題,也是大家開(kāi)發(fā)適配過(guò)程中大概率會(huì)遇到的問(wèn)題:
- Android Q 行為變更:所有應(yīng)用 (不管targetSdk是多少雇锡,對(duì)所有跑在Q設(shè)備上的應(yīng)用均有影響)
- Android Q 行為變更:以 Android Q 為目標(biāo)平臺(tái)的應(yīng)用(targetSDK == Q 才有影響)
- 項(xiàng)目升級(jí)遇到的問(wèn)題
一逛钻、Android Q 行為變更:所有應(yīng)用
官方文檔將這一部分內(nèi)容獨(dú)立于Q 行為變更:所有應(yīng)用來(lái)介紹,是因?yàn)檫@一部分內(nèi)容龐大且重要锰提,其中最大的更新就是用戶隱私權(quán)限變更曙痘。具體變更的權(quán)限如下:
從后臺(tái)啟動(dòng)Activity權(quán)限和無(wú)線掃描權(quán)限兩種權(quán)限的變更影響較少 ,本文就不做介紹立肘,詳細(xì)可以查看請(qǐng)查閱官方文檔:https://developer.android.com/preview/privacy/background-activity-starts
從后臺(tái)啟動(dòng)Activity權(quán)限變更僅針對(duì)與用戶毫無(wú)交互就啟動(dòng)一個(gè)Activity的情況边坤,(比如微信登陸授權(quán))。
本文將重點(diǎn)講述存儲(chǔ)權(quán)限,定位權(quán)限和設(shè)備標(biāo)識(shí)符三種權(quán)限的變更與適配谅年。
1.用戶存儲(chǔ)權(quán)限的變更
Android Q 在外部存儲(chǔ)設(shè)備中為每個(gè)應(yīng)用提供了一個(gè)“隔離存儲(chǔ)沙盒”(例如 /sdcard)茧痒。任何其他應(yīng)用都無(wú)法直接訪問(wèn)您應(yīng)用的沙盒文件。由于文件是您應(yīng)用的私有文件融蹂,因此您不再需要任何權(quán)限即可在外部存儲(chǔ)設(shè)備中訪問(wèn)和保存自己的文件旺订。此變更可讓您更輕松地保證用戶文件的隱私性弄企,并有助于減少應(yīng)用所需的權(quán)限數(shù)量。
沙盒区拳,簡(jiǎn)單而言就是應(yīng)用專(zhuān)屬文件夾拘领,并且訪問(wèn)這個(gè)文件夾無(wú)需權(quán)限。谷歌官方推薦應(yīng)用在沙盒內(nèi)存儲(chǔ)文件的地址為Context.getExternalFilesDir()下的文件夾樱调。比如要存儲(chǔ)一張圖片,則應(yīng)放在Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)中约素。
以下將按訪問(wèn)的目標(biāo)文件的地址介紹如何適配。
訪問(wèn)自己文件:Q中用更精細(xì)的媒體特定權(quán)限替換并取消了 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE權(quán)限本涕,并且無(wú)需特定權(quán)限业汰,應(yīng)用即可訪問(wèn)自己沙盒中的文件。
訪問(wèn)系統(tǒng)媒體文件:Q中引入了一個(gè)新定義媒體文件的共享集合菩颖,如果要訪問(wèn)沙盒外的媒體共享文件样漆,比如照片,音樂(lè)晦闰,視頻等放祟,需要申請(qǐng)新的媒體權(quán)限:READ_MEDIA_IMAGES,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO,申請(qǐng)方法同原來(lái)的存儲(chǔ)權(quán)限。
訪問(wèn)系統(tǒng)下載文件:對(duì)于系統(tǒng)下載文件夾的訪問(wèn)呻右,暫時(shí)沒(méi)做限制跪妥,但是,要訪問(wèn)其中其他應(yīng)用的文件声滥,必須允許用戶使用系統(tǒng)的文件選擇器應(yīng)用來(lái)選擇文件眉撵。
-
訪問(wèn)其他應(yīng)用沙盒文件:如果你的應(yīng)用需要使用其他應(yīng)用在沙盒內(nèi)創(chuàng)建的文件,請(qǐng)點(diǎn)擊使用其他應(yīng)用的文件,本文不做介紹落塑。
所以請(qǐng)判斷當(dāng)應(yīng)用運(yùn)行在Q平臺(tái)上時(shí)纽疟,取消對(duì)READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE兩個(gè)權(quán)限的申請(qǐng)。并替換為新的媒體特定權(quán)限憾赁。
當(dāng)滿足以下每個(gè)條件時(shí)污朽,將開(kāi)啟兼容模式,即不開(kāi)啟Q設(shè)備中的存儲(chǔ)權(quán)限改動(dòng):- 應(yīng)用targetSDK<=P龙考。
- 應(yīng)用安裝在從 Android P 升級(jí)到 Android Q 的設(shè)備上蟆肆。
但是當(dāng)應(yīng)用重新安裝(更新)時(shí),不會(huì)重新開(kāi)啟兼容模式晦款,存儲(chǔ)權(quán)限改動(dòng)將生效炎功。
所以按官方文檔所說(shuō),無(wú)論targetSDK是否為Q柬赐,必須對(duì)應(yīng)用進(jìn)行存儲(chǔ)權(quán)限改動(dòng)的適配亡问。
在測(cè)試中,當(dāng)targetSDK<=P,在Q Beat1版上申請(qǐng)兩個(gè)舊權(quán)限時(shí)會(huì)自動(dòng)改成申請(qǐng)三個(gè)新權(quán)限,不會(huì)影響應(yīng)用正常使用州藕,但當(dāng)targetSDK==Q時(shí)束世,申請(qǐng)舊權(quán)限將失敗并影響應(yīng)用正常使用。
2.用戶的定位權(quán)限的變更
為了讓用戶更好地控制應(yīng)用對(duì)位置信息的訪問(wèn)權(quán)限床玻,Android Q 引入了新的位置權(quán)限 ACCESS_BACKGROUND_LOCATION毁涉。
與現(xiàn)有的 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 權(quán)限不同,新權(quán)限僅會(huì)影響應(yīng)用在后臺(tái)運(yùn)行時(shí)對(duì)位置信息的訪問(wèn)權(quán)锈死。除非應(yīng)用的某個(gè) Activity 可見(jiàn)或應(yīng)用正在運(yùn)行前臺(tái)服務(wù)贫堰,否則應(yīng)用將被視為在后臺(tái)運(yùn)行。
與iOS系統(tǒng)一樣待牵,Q中也加入了后臺(tái)位置權(quán)限ACCESS_BACKGROUND_LOCATION其屏,如果應(yīng)用需要在后臺(tái)時(shí)也獲得用戶位置(比如滴滴),就需要?jiǎng)討B(tài)申請(qǐng)ACCESS_BACKGROUND_LOCATION權(quán)限缨该。
當(dāng)然如果不需要的話偎行,應(yīng)用就無(wú)需任何改動(dòng),且谷歌會(huì)按照應(yīng)用的targetSDK作出不同處理:
targetSDK <= P 應(yīng)用如果請(qǐng)求了ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION權(quán)限贰拿,Q設(shè)備會(huì)自動(dòng)幫你申請(qǐng)ACCESS_BACKGROUND_LOCATION權(quán)限蛤袒。
3.設(shè)備唯一標(biāo)識(shí)符的變更
從 Android Q 開(kāi)始,應(yīng)用必須具有 READ_PRIVILEGED_PHONE_STATE 簽名權(quán)限才能訪問(wèn)設(shè)備的不可重置標(biāo)識(shí)符(包含 IMEI 和序列號(hào))膨更。
許多用例不需要不可重置的設(shè)備標(biāo)識(shí)符妙真。如果您的應(yīng)用沒(méi)有該權(quán)限,但您仍嘗試查詢標(biāo)識(shí)符的相關(guān)信息荚守。會(huì)返回空值或報(bào)錯(cuò)珍德。
設(shè)備唯一標(biāo)識(shí)符需要特別注意,原來(lái)的READ_PHONE_STATE權(quán)限已經(jīng)不能獲得IMEI和序列號(hào)矗漾,如果想在Q設(shè)備上通過(guò)使用以下代碼獲取設(shè)備的ID
((TelephonyManager)getActivity().getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId()
則執(zhí)行以上代碼會(huì)返回空值(targetSDK<=P)或者報(bào)錯(cuò)(targetSDK==Q)菱阵。且官方所說(shuō)的READ_PRIVILEGED_PHONE_STATE權(quán)限只提供給系統(tǒng)app,所以這個(gè)方法行不通了缩功。
谷歌官方給予了設(shè)備唯一ID最佳做法,但是此方法給出的ID可變都办,可以按照具體需求具體解決嫡锌。本文給出一個(gè)不變和基本不重復(fù)的UUID方法。
public static String getUUID() {
String serial = null;
String m_szDevIDShort = "35" +
Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +
Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +
Build.USER.length() % 10; //13 位
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
serial = android.os.Build.getSerial();
} else {
serial = Build.SERIAL;
}
//API>=9 使用serial號(hào)
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
//serial需要一個(gè)初始化
serial = "serial"; // 隨便一個(gè)初始化
}
//使用硬件信息拼湊出來(lái)的15位號(hào)碼
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
雖然由于唯一標(biāo)識(shí)符權(quán)限的更改會(huì)導(dǎo)致android.os.Build.getSerial()返回unknown,但是由于m_szDevIDShort是由硬件信息拼出來(lái)的琳钉,所以仍然保證了UUID的唯一性和持久性势木。
經(jīng)測(cè)試上述方法完全相同的手機(jī)有可能重復(fù),網(wǎng)上還有其他方案比如androidID,但是androidID可能由于機(jī)型原因返回null歌懒,所以個(gè)人任務(wù)兩種方法半斤八兩啦桌。設(shè)備ID的獲取一個(gè)版本比一個(gè)版本艱難,如果有好的方法歡迎指出。
4.關(guān)于minSDK警告
在 Android Q 中甫男,當(dāng)用戶首次運(yùn)行以 Android 6.0(API 級(jí)別 23)以下的版本為目標(biāo)平臺(tái)的任何應(yīng)用時(shí)且改,Android平臺(tái)會(huì)向用戶發(fā)出警告。如果此應(yīng)用要求用戶授予權(quán)限板驳,則系統(tǒng)會(huì)先向用戶提供調(diào)整應(yīng)用權(quán)限的機(jī)會(huì)又跛,然后才會(huì)允許此應(yīng)用首次運(yùn)行。谷歌要求運(yùn)行在Q設(shè)備上的應(yīng)用targetSDK>=23,不然會(huì)向用戶發(fā)出警告若治。
二慨蓝、Android Q 行為變更:以 Android Q 為目標(biāo)平臺(tái)的應(yīng)用
非 SDK 接口限制
非SDK接口限制在Android P中就已提出,但是在Q中端幼,被限制的接口的分類(lèi)有較大變化礼烈。
非SDK接口介紹
為了確保應(yīng)用穩(wěn)定性和兼容性,Android 平臺(tái)開(kāi)始限制您的應(yīng)用可在 Android 9(API 級(jí)別 28)中使用哪些非 SDK 接口婆跑。Android Q 包含更新后的受限非 SDK 接口列表(基于與 Android 開(kāi)發(fā)者之間的協(xié)作以及最新的內(nèi)部測(cè)試)此熬。
非SDK接口限制就是某些SDK中的私用方法,如private方法洽蛀,你通過(guò)Java反射等方法獲取并調(diào)用了摹迷。那么這些調(diào)用將在target>=P或target>=Q的設(shè)備上被限制使用,當(dāng)你使用了這些方法后郊供,會(huì)報(bào)錯(cuò):
非SDK接口查找
如果您不確定自己的應(yīng)用是否使用了非 SDK 接口峡碉,則可以測(cè)試該應(yīng)用進(jìn)行確認(rèn)。當(dāng)你調(diào)用了非SDK接口時(shí)驮审,會(huì)有類(lèi)似Accessing hidden XXX的日志:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
但是一個(gè)大項(xiàng)目到底哪里使用了這些方法鲫寄,靠review代碼和看日志肯定是不現(xiàn)實(shí)的,谷歌官方也提供了官方檢查器veridex用來(lái)檢測(cè)一個(gè)apk中哪里使用了非SDK接口疯淫。veridex下載地来。https://android.googlesource.com/platform/prebuilts/runtime/+/master/appcompat,其中有windows,linux和mac版本熙掺,對(duì)應(yīng)下載即可未斑。下載解壓后命令行cd到veridex目錄下使用./appcompat.sh --dex-file=Q.apk即可自動(dòng)掃描。Q.apk為包的絕對(duì)路徑币绩,如果包與veridex在相同目錄下直接輸入包文件名即可蜡秽。掃描結(jié)果分為兩部分,一部分為被調(diào)用的非SDK接口的位置缆镣,另一部分為非SDK接口數(shù)量統(tǒng)計(jì)芽突。
非SDK接口適配
如果您的應(yīng)用依賴(lài)于非 SDK 接口,則應(yīng)該開(kāi)始計(jì)劃遷移到 SDK 替代方案董瞻。如果您無(wú)法為應(yīng)用中的某項(xiàng)功能找到使用非 SDK 接口的替代方案寞蚌,則應(yīng)該請(qǐng)求新的公共 API。
官方要求targetSDK>=P的應(yīng)用不使用這些方法,并尋找其他的公共API去替代這些非SDK接口挟秤,如果找不到壹哺,則可以向谷歌申請(qǐng),請(qǐng)求一個(gè)新的公共API https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces#feature-request (一般不需要)煞聪。
就我個(gè)人掃描并定位的結(jié)果來(lái)看斗躏,項(xiàng)目中使用非SDK接口大概率有以下兩種情況:
- 在自定義View的過(guò)程中為了方便,使用反射修改某個(gè)參數(shù)昔脯。
- 三方SDK中使用了非SDK接口(這種情況比較多)啄糙。
第一種是好解決的,畢竟是我們自己寫(xiě)的代碼云稚。第二種就頭疼了隧饼,只能更新到最新的三方SDK版本,或者提工單静陈、換庫(kù)(也是整個(gè)適配過(guò)程中工作量最龐大的部分)燕雁。
三、Android項(xiàng)目升級(jí)遇到的問(wèn)題
模擬器X86鲸拥,項(xiàng)目中SO庫(kù)為v7
- 找到so庫(kù)源代碼拐格,編譯成x86
- 如果so庫(kù)只是某個(gè)功能點(diǎn)使用,對(duì)APP整體沒(méi)大影響刑赶,就可以屏蔽特定so庫(kù)功能或略過(guò)測(cè)試
- 如果so庫(kù)是項(xiàng)目核心庫(kù)必須加載捏浊,也可使用騰訊云測(cè),上面有谷歌親兒子Q版本撞叨。騰訊云測(cè)有adb遠(yuǎn)程連接調(diào)試功能(我沒(méi)成功過(guò))金踪。adb連不上也沒(méi)關(guān)系,直接安裝就行牵敷,云測(cè)上也可以直接看日志胡岔。
- 至于inter的houdini我嘗試研究過(guò),理論上能安裝在x86模擬器上讓它編譯v7的so庫(kù)枷餐,但是由于關(guān)于houdini的介紹比較少也比較舊靶瘸,建議大家時(shí)間不充裕的話就別研究了。
Requires development platform Q but this is a release platform.
由于目前Q是preview版毛肋,所以targetSDK==Q 的應(yīng)用只能在Q設(shè)備上跑奕锌。
INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2
這個(gè)錯(cuò)誤是由于打包壓縮so庫(kù)時(shí)造成的,具體原因可見(jiàn):https://issuetracker.google.com/issues/37045367
在AndroidManifest.xml的application節(jié)點(diǎn)下加入android:extractNativeLibs="true"
可能有人加了上面代碼還是不行村生,在app/build.gradle中的defaultConfig節(jié)點(diǎn)下加入
packagingOptions{
doNotStrip "/armeabi/.so" doNotStrip "/armeabi-v7a/.so" doNotStrip "/x86/.so" }
Didn't find class “org.apache.http.client.methods.HttpPost"
在AndroidManifest.xml的application節(jié)點(diǎn)下加入:
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
如果你的項(xiàng)目沒(méi)有適配過(guò)android O或P,那么你需要注意:
- android O的讀取已安裝應(yīng)用權(quán)限(對(duì)應(yīng)用內(nèi)自動(dòng)更新有影響)
- android P的默認(rèn)禁止訪問(wèn)http的API
這兩個(gè)版本的適配問(wèn)題本文就不做詳述饼丘,大家可以查看網(wǎng)上詳細(xì)的介紹趁桃。
以上就是Android Q(10.0)版本新特性和兼容性適配,中間會(huì)遇到各種各樣的坑,還是建議詳細(xì)查看Google的官方文檔卫病。
本文的參考文檔:
Android Q Beta開(kāi)發(fā)者文檔鏈接::https://developer.android.com/preview
Android Q Beta鏡像下載鏈接:https://developer.android.com/preview/download
Android Q Beta 發(fā)布 blog:https://android-developers.googleblog.com/2019/03/introducing-android-q-beta.html