背景
近期,Google Play向所有的應(yīng)用開發(fā)者發(fā)送了一封郵件,郵件中明確了應(yīng)用將目標(biāo)API級別更新為Android 12的最后期限。
這表示,Android 12的適配工作不得不提上日程了。
本著將改動最小化的原則,建議在開始適配之前,可以將此次需要適配的一系列行為變更,按照對自身項目的影響程度,先進行優(yōu)先級劃分,并依據(jù)此優(yōu)先級去分批、逐步完成適配工作。
該如何進行劃分呢?可能具體到每個人的項目情況都有所不同,這里提供一下筆者項目的適配任務(wù)優(yōu)先級劃分標(biāo)準(zhǔn),供其他開發(fā)者參考:
P0:功能異常
指明確影響到了App功能的正常使用,甚至導(dǎo)致App無法正常啟動或者崩潰率直線上升的行為變更。
P1:體驗下降
指雖不影響功能正常使用,但可能導(dǎo)致獲取數(shù)據(jù)的準(zhǔn)確度下降,進而影響運營判斷;或者需要增加操作步驟,進而影響用戶體驗的行為變更。
P2:可選優(yōu)化
指對App本身沒有什么實質(zhì)影響,但做好適配后可以增強App健壯性,或者可以提高用戶體驗的行為變更。
接下來,我們就以上面劃分好的優(yōu)先級標(biāo)準(zhǔn),來開始對Android 12的適配工作。
影響所有App的行為變更
P0:功能異常
應(yīng)用啟動畫面
造成影響
簡單講,就是從Android 12開始,所有的App在每次啟動時(特指冷啟動與溫啟動),系統(tǒng)都會為我們加上一個默認(rèn)的啟動畫面,如下所示:
該啟動畫面主要由以下4個元素組成,分別為:
(1) 應(yīng)用圖標(biāo):可以是靜態(tài)或動畫形式。默認(rèn)情況下,使用Launcher圖標(biāo)。
(2) 圖標(biāo)背景:可選,在圖標(biāo)與窗口背景之間需要更高的對比度時很有用。
(3) 前景遮罩:可選,前景的 ? 將被遮蓋。
(4) 窗口背景:不透明的單色,默認(rèn)是所設(shè)置主題的windowBackground。
雖然這個啟動畫面允許我們一定程度的自定義,但總體都無法跳脫出以上4個元素,且無法去除。如果不做任何處理,加上我們原有的閃屏頁和廣告頁,視覺上會有多個啟動畫面。
適配方案
方案1(懶人專用):設(shè)置除窗口背景之外的元素都為透明
處理后的效果就是,在啟動時會先顯示由所設(shè)置主題的windowBackground指定的純色背景,即與大多數(shù)開發(fā)者之前為了解決啟動黑屏/白屏問題所采用的方法一致。
方案2(常規(guī)做法):改用SplashScreen API定制系統(tǒng)啟動畫面
缺點就是可定制程度低,可能無法滿足產(chǎn)品的需求;
如果總體的效果可以接受,那么接下來要處理的就是對原有閃屏頁的取舍,以及與原有廣告頁的畫面銜接了。
但如開頭所言,我們的目標(biāo)是將改動最小化,那么,原有閃屏頁該干嘛還是讓它干嘛,初始化也好,路由也罷,邏輯不變,要求只是不再顯示而已。
具體做法如下:
- 添加 SplashScreen compat 庫
- 在閃屏頁調(diào)用 SplashScreen#setKeepOnScreenCondition{ true } 使得默認(rèn)的啟動畫面持續(xù)覆蓋原有的閃屏頁,直到廣告頁開始顯現(xiàn)時,才調(diào)用SplashScreen#setKeepOnScreenCondition{ false } 讓頁面重新顯示,以此實現(xiàn)平穩(wěn)過渡
麥克風(fēng)和攝像頭切換開關(guān)
造成影響
簡單講,就是從Android 12開始,用戶可以通過狀態(tài)欄下拉菜單中兩個新增的切換開關(guān)選項,一鍵啟用/停用攝像頭和麥克風(fēng)使用權(quán)限。
請注意,這里的「使用權(quán)限」針對的是設(shè)備上的所有App,是全局的,不要和Android 6.0的「運行時權(quán)限」混淆。
而兩者在具體表現(xiàn)上也有所不同,在實際操作中:
當(dāng)關(guān)閉攝像頭使用權(quán)限后,畫面錄制將繼續(xù)進行,但只會收到空白畫面;
當(dāng)關(guān)閉麥克風(fēng)使用權(quán)限后,聲音錄制將繼續(xù)進行,但只會收到無聲視頻。
適配方案
盡管官網(wǎng)上提供了檢查設(shè)備是否支持麥克風(fēng)和攝像頭切換開關(guān)的API,也就是檢查狀態(tài)欄下拉菜單是否有這兩個開關(guān)選項,然而這對于我們實際的適配工作幾乎沒有什么卵用:
SensorPrivacyManager類倒是有提供檢查指定切換開關(guān)是否開啟的API,但由于是系統(tǒng)權(quán)限,因此即使是通過反射形式也無法調(diào)用:
所幸的是,如果用戶主動關(guān)閉了攝像頭或麥克風(fēng)的使用權(quán)限,那么當(dāng)下次App再需要啟動攝像頭或麥克風(fēng)時,系統(tǒng)就會提醒用戶,相關(guān)硬件的使用權(quán)限已關(guān)閉,并申請重新開啟:
因此,對于此行為變更的適配,我們要做的,就是驗證在用戶主動關(guān)閉了攝像頭或麥克風(fēng)使用權(quán)限后,App的相關(guān)功能是否受影響,至于監(jiān)聽/提示/重新開啟的工作則交給系統(tǒng)幫我們完成即可。
影響目標(biāo)API級別為Android 12的App的行為變更
P0:功能異常
更安全的組件導(dǎo)出
造成影響
簡單講,就是以Android 12為目標(biāo)平臺的App,如果其包含的四大組件中使用到了Intent過濾器(intent-filter),則必須顯式聲明 android:exported 屬性,否則App將無法在Android 12及更高系統(tǒng)版本的設(shè)備上安裝:
適配方案
這里要區(qū)分兩種情況:
如果是自身項目使用到了,則按要求顯式聲明即可;
如果是依賴的第三方庫使用到了,那就比較麻煩了。且不說等待第三方庫更新的過程很被動,如果該庫還已停止維護了,那就芭比Q了。
但眾所周知,Android Studio Project在打包時是會合并多個Module的AndroidManifest.xml文件的,基于這種情況,我們就可以通過編寫Gradle腳本,在打包過程中檢索合并后的AndroidManifest.xml文件中,所有使用到了Intent過濾器但沒有聲明exported屬性的組件,動態(tài)地為其加上exported屬性。
具體的Gradle腳本已經(jīng)有人寫過了,這里就不再貼出了:
http://events.jianshu.io/p/1913b48f2dad
待處理 intent 可變性
造成影響
簡單講,就是以Android 12為目標(biāo)平臺的App,在構(gòu)建PendingIntent時,需要指定Flag為FLAG_IMMUTABLE(建議)或FLAG_MUTABLE二者之一,否則App將崩潰并出現(xiàn)以下警告。
適配方案
但同樣可能出現(xiàn)第三方庫的代碼未正確指定Flag的問題,目前除了等待第三方庫更新之外似乎也沒有更好的措施。
前臺服務(wù)啟動限制
造成影響
簡單講,就是以Android 12為目標(biāo)平臺的App,如果嘗試在后臺運行時啟動前臺服務(wù)(startForegroundService),則會引發(fā)ForegroundServiceStartNotAllowedException異常(某些場景除外):
適配方案
分兩步走:
- 檢查App是否有在后臺啟動前臺服務(wù)的行為
可在Terminal終端執(zhí)行以下adb命令,該命令會監(jiān)控你的App是否有在后臺啟動前臺服務(wù)的行為,一旦有此行為,就會在通知欄推送一條提醒,定位到觸發(fā)此行為的代碼處:
adb shell device_config put activity_manager \ default_fgs_starts_restriction_notification_enabled true
- 考慮改用WorkManager的加急工作來執(zhí)行后臺任務(wù)
精確的鬧鐘權(quán)限
造成影響
簡單講,就是以Android 12為目標(biāo)平臺的App,如果使用到了AlarmManager來設(shè)置定時任務(wù),并且設(shè)置的是精準(zhǔn)的鬧鐘(使用了setAlarmClock()、setExact()、setExactAndAllowWhileIdle()這幾種方法),則需要確保SCHEDULE_EXACT_ALARM權(quán)限聲明且打開,否則App將崩潰并出現(xiàn)以下警告:
適配方案
分三步走:
- 在AndroidManifest.xml清單文件中聲明 SCHEDULE_EXACT_ALARM 權(quán)限
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
- 判斷是否具有設(shè)置精確鬧鐘的權(quán)限
- 打開鬧鐘和提醒權(quán)限授權(quán)頁面,進行授權(quán)
通知 trampoline 限制
造成影響
簡單講,就是我們之前在配置通知(Notification)的點按行為時,可能會通過PendingIntent來啟動一個Service或BrocastReceiver。而以Android 12為目標(biāo)平臺的App,如果嘗試在Service或BrocastReceiver中內(nèi)調(diào)用 startActivity(),系統(tǒng)會阻止該Activity啟動,并在 Logcat 中顯示以下消息:
適配方案
分兩步走:
- 排查哪個Service或BrocastReceiver有此行為
可在Terminal終端執(zhí)行以下adb命令,該命令會在你點按通知后,識別哪個Service或BrocastReceiver調(diào)用了startActivity(),并輸出相關(guān)信息到Logcat,可以通過關(guān)鍵字“NotifInteractionLog”進行過濾:
adb shell dumpsys activity service \ com.android.systemui/.dump.SystemUIAuxiliaryDumpService
- 考慮在配置通知(Notification)的點按行為時選擇直接啟動Activity
P1:體驗下降
大致位置
造成影響
做過定位功能的Android開發(fā)者都知道,Android提供了兩種不同精確度的位置權(quán)限,分別是:
-
ACCESS_COARSE_LOCATION(大致位置)
提供設(shè)備位置的估算值,將范圍限定在大約 1.6 公里(1 英里)內(nèi)
-
ACCESS_FINE_LOCATION(確切位置)
通常將范圍限定在大約 50 米(160 英尺)內(nèi),有時精確到幾米(10 英尺)范圍以內(nèi)
而在以Android 12為目標(biāo)平臺的App上,當(dāng)App嘗試請求ACCESS_FINE_LOCATION權(quán)限時,系統(tǒng)權(quán)限對話框會提供兩個選項,即允許App獲取確切位置,還是僅允許獲取大致位置。
也即是說,給了用戶拒絕提供確切位置的權(quán)力,一旦用戶拒絕,這種情況下App就只能獲取到大致位置了。
適配方案
雖然用戶可能拒絕提供確切位置,但我們依舊可以再次請求升級到確切位置:
當(dāng)然,在再次請求前提供一個適當(dāng)?shù)慕忉屨f明是一個比較好的做法,App本身也要做好只能獲取到大致位置時的業(yè)務(wù)降級處理。
應(yīng)用休眠
造成影響
簡單講,就是以Android 12為目標(biāo)平臺的App,如果用戶有長達幾個月的時間沒有打開過你的App,那么你之前申請的所有運行時權(quán)限都會被重置為未授權(quán)狀態(tài),即使再次打開也無法恢復(fù),需要重新申請。
適配方案
基本上,只要你的App之前已經(jīng)做好運行時權(quán)限的的判斷和申請,那對你的App就幾乎沒什么影響。如果還是想穩(wěn)妥的測試一下,可以用Terminal終端執(zhí)行adb命令,手動觸發(fā)應(yīng)用休眠:
https://developer.android.com/topic/performance/app-hibernation#manually-invoke
自定義通知
造成影響
簡單講,就是如果之前App中的通知(Notification)中使用到了自定義內(nèi)容視圖,并且該視圖是填滿整個通知區(qū)域的。那么當(dāng)App以Android 12為目標(biāo)平臺后,視圖將不再能填充整個區(qū)域,而是會被縮小到某個固定范圍:
另外,所有通知現(xiàn)在都變成了可展開的,如果你之前設(shè)置自定義內(nèi)容視圖時使用的是 setCustomContentView方法,那你現(xiàn)在則還需要另外再使用setBigCustomContentView方法來設(shè)置展開狀態(tài)的樣式,以確保通知在收起狀態(tài)和展開狀態(tài)的樣式能統(tǒng)一。
適配方案
確認(rèn)被縮減顯示范圍后的自定義內(nèi)容視圖樣式是否能接受,若不能接受,則根據(jù)實際需要調(diào)整即可。
演示Demo
這邊提供了一個Demo,可以幫助我們在實際操作中,快速理解以上行為變更的具體表現(xiàn),并且由于是輕量級的Demo,所以也方便我們進行改動,快速驗證適配方案的效果:
https://github.com/madchan/SupportAndroid12
「椎鋒陷陳」微信技術(shù)號現(xiàn)已開通,為了獲得第一手的技術(shù)文章推送,歡迎搜索關(guān)注!