Android 12 保姆級適配指南來啦!

語言: CN / TW / HK

之前還推動過一篇,歡迎一起查閱。

分享一下適配 Android 12 遇到的坑

背景

近期,Google Play向所有的應用開發者傳送了一封郵件,郵件中明確了應用將目標API級別更新為Android 12的最後期限。

這表示,Android 12的適配工作不得不提上日程了。

本著將改動最小化的原則,建議在開始適配之前,可以將此次需要適配的一系列行為變更,按照對自身專案的影響程度,先進行優先順序劃分,並依據此優先順序去分批、逐步完成適配工作。

該如何進行劃分呢?可能具體到每個人的專案情況都有所不同,這裡提供一下筆者專案的適配任務優先順序劃分標準,供其他開發者參考:

P0:功能異常

指明確影響到了App功能的正常使用,甚至導致App無法正常啟動或者崩潰率直線上升的行為變更。

P1:體驗下降

指雖不影響功能正常使用,但可能導致獲取資料的準確度下降,或者需要增加操作步驟進而影響使用者體驗的行為變更。

P2:可選優化

指對App本身沒有什麼實質影響,但做好適配後可以增強App健壯性或使用者體驗的行為變更。

接下來,我們就以上面劃分好的優先順序標準,來開始對Android 12的適配工作。

1

影響所有App的行為變更

P0:功能異常

1. 應用啟動畫面

造成影響

簡單講,就是從Android 12開始,所有的App在每次啟動時(特指冷啟動與溫啟動),系統都會為我們加上一個預設的啟動畫面,如下所示:

該啟動畫面主要由以下4個元素組成,分別為:

(1) 應用圖示 :可以是靜態或動畫形式。預設情況下,使用Launcher圖示。

(2) 圖示背景 :可選,在圖示與視窗背景之間需要更高的對比度時很有用。

(3) 前景遮罩 :可選,前景的 ⅓ 將被遮蓋。

(4) 視窗背景 :不透明的單色,預設是所設定主題的 windowBackground

雖然這個啟動畫面允許我們一定程度的自定義,但總體都無法跳脫出以上4個元素,且無法去除。如果不做任何處理,加上我們原有的閃屏頁和廣告頁,視覺上會有多個啟動畫面。

適配方案

方案1(懶人專用): 設定除視窗背景之外的元素都為透明。

處理後的效果就是,在啟動時會先顯示由所設定主題的 windowBackground 指定的純色背景,即與大多數開發者之前為了解決啟動黑屏/白屏問題所採用的方法一致。

方案2(常規做法): 改用SplashScreen API定製系統啟動畫面。

缺點就是可定製程度低,可能無法滿足產品的需求;

如果總體的效果可以接受,那麼接下來要處理的就是對原有閃屏頁的取捨,以及與原有廣告頁的畫面銜接了。

但如開頭所言,我們的目標是 將改動最小化 ,那麼,原有閃屏頁該幹嘛還是讓它幹嘛,初始化也好,路由也罷,邏輯不變,要求只是不再顯示而已。

具體做法如下:

1. 新增 SplashScreen compat 庫。

2. 在閃屏頁呼叫 SplashScreen#setKeepOnScreenCondition{ true } 使得預設的啟動畫面持續覆蓋原有的閃屏頁,直到廣告頁開始顯現時,才呼叫 SplashScreen#setKeepOnScreenCondition{ false } 讓頁面重新顯示,以此實現平穩過渡。

2. 麥克風和攝像頭切換開關

造成影響

簡單講,就是從Android 12開始,使用者可以通過狀態列下拉選單中兩個新增的切換開關選項,一鍵啟用/停用攝像頭和麥克風使用許可權。

請注意,這裡的「使用許可權」針對的是裝置上的所有App,是全域性的,不要和Android 6.0的「執行時許可權」混淆。

而兩者在具體表現上也有所不同,在實際操作中:

• 當關閉攝像頭使用許可權後,畫面錄製將繼續進行,但 只會收到空白畫面

• 當關閉麥克風使用許可權後,聲音錄製將繼續進行,但 只會收到無聲影片

適配方案

儘管官網上提供了檢查裝置是否支援麥克風和攝像頭切換開關的API,也就是檢查狀態列下拉選單是否有這兩個開關選項,然而這對於我們實際的適配工作幾乎沒有什麼卵用:

SensorPrivacyManager 類倒是有提供檢查指定切換開關是否開啟的API,但由於是系統許可權,因此即使是通過反射形式也無法呼叫:

所幸的是,如果使用者主動關閉了攝像頭或麥克風的使用許可權,那麼當下次App再需要啟動攝像頭或麥克風時,系統就會提醒使用者,相關硬體的使用許可權已關閉,並申請重新開啟:

因此,對於此行為變更的適配,我們要做的,就是驗證在使用者主動關閉了攝像頭或麥克風使用許可權後,App的相關功能是否受影響,至於監聽/提示/重新開啟的工作則交給系統幫我們完成即可。

2

影響目標API級別為Android 12的App的行為變更

P0:功能異常

1. 更安全的元件匯出

造成影響

簡單講,就是以Android 12為目標平臺的App,如果其包含的四大元件中使用到了Intent過濾器(intent-filter),則必須顯式宣告 android:exported 屬性,否則App將 無法在Android 12及更高系統版本的裝置上安裝

適配方案

這裡要區分兩種情況:

1. 如果是自身專案使用到了,則按要求顯式宣告即可;

2. 如果是依賴的第三方庫使用到了,那就比較麻煩了。且不說等待第三方庫更新的過程很被動,如果該庫還已停止維護了,那就芭比Q了。

但眾所周知,Android Studio Project在打包時是會合並多個Module的 AndroidManifest.xml 檔案的,基於這種情況,我們就可以通過編寫Gradle指令碼,在打包過程中檢索合並後的 AndroidManifest.xml 檔案中,所有使用到了Intent過濾器但沒有宣告exported屬性的元件,動態地為其加上exported屬性。

具體的Gradle指令碼 已經有人寫過了,這裡就不再貼出了:

http://events.jianshu.io/p/1913b48f2dad

2. 待處理 intent 可變性

造成影響

簡單講,就是以Android 12為目標平臺的App,在構建PendingIntent時,需要指定Flag為 FLAG_IMMUTABLE (建議)或 FLAG_MUTABLE 二者之一,否則App將崩潰並出現以下警告。

適配方案

但同樣可能出現第三方庫的程式碼未正確指定Flag的問題,目前除了等待第三方庫更新之外似乎也沒有更好的措施。

3. 前臺服務啟動限制

造成影響

簡單講,就是以Android 12為目標平臺的App,如果嘗試在後臺執行時啟動前臺服務( startForegroundService ),則會引發 ForegroundServiceStartNotAllowedException 異常( 某些場景除外 ):

https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions

適配方案

分兩步走:

1. 檢查App是否有在後臺啟動前臺服務的行為

可在Terminal終端執行以下adb命令,該命令會監控你的App是否有在後臺啟動前臺服務的行為,一旦有此行為,就會在通知欄推送一條提醒,定位到觸發此行為的程式碼處:

adb shell device_config put activity_manager \ default_fgs_starts_restriction_notification_enabled true

2. 考慮改用WorkManager的加急工作來執行後臺任務

4. 精確的鬧鐘許可權

造成影響

簡單講,就是以Android 12為目標平臺的App,如果使用到了AlarmManager來設定定時任務,並且設定的是精準的鬧鐘(使用了 setAlarmClock() setExact() setExactAndAllowWhileIdle() 這幾種方法),則需要確保 SCHEDULE_EXACT_ALARM 許可權宣告且開啟,否則App將崩潰並出現以下警告:

適配方案

分三步走:

1. 在 AndroidManifest.xml 清單檔案中宣告 SCHEDULE_EXACT_ALARM 許可權。

2. 判斷是否具有設定精確鬧鐘的許可權。

3. 開啟鬧鐘和提醒許可權授權頁面,進行授權。

5. 通知 trampoline 限制

造成影響

簡單講,就是我們之前在配置通知(Notification)的點按行為時,可能會通過PendingIntent來啟動一個Service或BrocastReceiver。而以Android 12為目標平臺的App,如果嘗試在Service或BrocastReceiver中內呼叫 startActivity() ,系統會阻止該Activity啟動,並在 Logcat 中顯示以下訊息:

適配方案

分兩步走:

1. 排查哪個Service或BrocastReceiver有此行為

可在Terminal終端執行以下adb命令,該命令會在你點按通知後,識別哪個Service或BrocastReceiver呼叫了 startActivity() ,並輸出相關資訊到Logcat,可以通過關鍵字“NotifInteractionLog”進行過濾:

adb shell dumpsys activity service \  com.android.systemui/.dump.SystemUIAuxiliaryDumpService

2. 考慮在配置通知(Notification)的點按行為時選擇直接啟動Activity

P1:體驗下降

1. 大致位置

造成影響

做過定位功能的Android開發者都知道,Android提供了兩種不同精確度的位置許可權,分別是:

• A CCESS_COARSE_LOCATION (大致位置)

提供裝置位置的估算值,將範圍限定在大約 1.6 公里(1 英里)內。

•  ACCESS_FINE_LOCATION (確切位置)

通常將範圍限定在大約 50 米(160 英尺)內,有時精確到幾米(10 英尺)範圍以內。

而在以Android 12為目標平臺的App上,當App嘗試請求 ACCESS_FINE_LOCATION 許可權時,系統許可權對話方塊會提供兩個選項,即允許App獲取確切位置,還是僅允許獲取大致位置。

也即是說,給了使用者拒絕提供確切位置的權力,一旦使用者拒絕,這種情況下App就只能獲取到大致位置了。

適配方案

雖然使用者可能拒絕提供確切位置,但我們依舊可以再次請求升級到確切位置:

當然,在再次請求前提供一個適當的解釋說明是一個比較好的做法,App本身也要做好只能獲取到大致位置時的業務降級處理。

2. 應用休眠

造成影響

簡單講,就是以Android 12為目標平臺的App,如果使用者有長達幾個月的時間沒有開啟過你的App,那麼你之前申請的所有執行時許可權都會被重置為未授權狀態,即使再次開啟也無法恢復,需要重新申請。

適配方案

基本上,只要你的App之前已經做好執行時許可權的的判斷和申請,那對你的App就幾乎沒什麼影響。如果還是想穩妥的測試一下,可以用Terminal終端執行adb命令,手動觸發應用休眠:

https://developer.android.com/topic/performance/app-hibernation#manually-invoke

3. 自定義通知

造成影響

簡單講,就是如果之前App中的通知(Notification)中使用到了自定義內容檢視,並且該檢視是填滿整個通知區域的。那麼當App以Android 12為目標平臺後,檢視將不再能填充整個區域,而是會被縮小到某個固定範圍:

另外,所有通知現在都變成了可展開的,如果你之前設定自定義內容檢視時使用的是 setCustomContentView 方法,那你現在則還需要另外再使用 setBigCustomContentView 方法來設定展開狀態的樣式,以確保通知在收起狀態和展開狀態的樣式能統一。

適配方案

確認被縮減顯示範圍後的自定義內容檢視樣式是否能接受,若不能接受,則根據實際需要調整即可。

3

演示Demo

這邊提供了一個Demo,可以幫助我們通過實際操作,快速理解以上行為變更的具體表現,並且由於是輕量級的Demo,所以也方便我們進行改動,快速驗證適配方案的效果:

https://github.com/madchan/SupportAndroid12

最後推薦一下我做的網站,玩Android:  wanandroid.com ,包含詳盡的知識體系、好用的工具,還有本公眾號文章合集,歡迎體驗和收藏!

推薦閱讀

Android卡頓掉幀?努比亞技術團隊分享

根據手機桌布自動切換App主題,它真的來了!

APP啟動優化,監控與實踐一起來!

點選  關注我的公眾號

如果你想要跟大家分享你的文章,歡迎投稿~

┏(^0^)┛明天見!