前言
本文主要針對Android O的適配,文中大部分內容將來自官網,本文只是總結提取出適配需要的注意點,關于Android O的一些新Feature不會提及。總的來說,Android O基本是純后臺應用的噩夢,此類應用的適配多數要面臨重構、大改的窘境,而對于前臺應用適配則相對輕松。
適配要點
總體而言Android O的適配主要注意以下三點:
1、后臺服務訪問受限,Android O不允許后臺應用創建后臺服務
2、諸多隱式廣播被砍,清單文件靜態注冊無效,只能動態注冊
3、運行時權限授權機制修改,只授權申請的權限,而非整個權限組的權限
后臺服務訪問受限
前文提及,Android O不允許后臺應用創建后臺服務,所以先來看看Android系統是如何區分前臺與后臺應用的。
1、前、后臺應用區分
系統可以區分 前臺 和 后臺 應用。 (用于服務限制目的的后臺定義與內存管理使用的定義不同;一個應用按照內存管理的定義可能處于后臺,但按照Android 8上能夠啟動服務的定義又處于前臺。)如果滿足以下任意條件,應用將被視為處于前臺:
1、具有可見 Activity(不管該 Activity 已啟動還是已暫停)
2、具有前臺服務
3、另一個前臺應用已關聯到該應用(不管是通過綁定到其中一個服務,還是通過使用其中一個內容提供程序)。 例如,如果另一個應用綁定到該應用的服務,那么該應用處于前臺:
IME
壁紙服務
通知偵聽器
語音或文本服務
如果以上條件均不滿足,應用將被視為處于后臺。
以上內容摘自官網,首次閱讀你可能不太理解“一個應用按照內存管理的定義可能處于后臺,但按照能夠啟動服務的定義又處于前臺”,即此處的后臺定義與后臺進程定義稍有出入。
內存管理中對后臺進程的定義如下:
包含目前對用戶不可見的 Activity 的進程(已調用 Activity 的 onStop()方法。這些進程對用戶體驗沒有直接影響,系統可能隨時終止它們,以回收內存供前臺進程、可見進程或服務進程使用。
Android 8上能夠啟動服務定義中的后臺:
即不滿足上文所述三點中的任何一點應用被視為后臺應用,留意第三點中的“不管是通過綁定到其中一個服務,還是通過使用其中一個內容提供程序”,在內存管理中并未關聯ContentProvider來分類進程。
假設我們有一個后臺進程,此時有個前臺應用關聯訪問了我們ContentProvider,那么此時,我們的后臺進程將不會被系統判定為后臺應用,而是前臺應用。這就是“一個應用按照內存管理的定義可能處于后臺,但按照能夠啟動服務的定義又處于前臺”的一個具體場景。
結論:一個后臺進程,在Android O上不一定被判定為后臺應用。
2、后臺服務限制
在了解Android O如何區分前、后臺應用后,接下來看看如果后臺應用強行創建后臺服務會如何。
如果針對 Android O 的應用嘗試在不允許其創建后臺服務的情況下使用 startService() 函數,則該函數將引發一個 IllegalStateException。
新的 Context.startForegroundService() 函數將啟動一個前臺服務。現在,即使應用在后臺運行,系統也允許其調用 Context.startForegroundService()。不過,應用必須在創建服務后的五秒內調用該服務的 startForeground() 函數。
當應用處于前臺時,應用可以自由創建和運行前臺服務與后臺服務。 進入后臺時,在一個持續數分鐘的時間窗內,應用仍可以創建和使用服務。
在該時間窗結束后,應用將被視為處于 空閑 狀態。 此時,系統將停止應用的后臺服務,就像應用已經調用服務的Service.stopSelf()方法。
在這些情況下,后臺應用將被置于一個臨時白名單中并持續數分鐘。 位于白名單中時,應用可以無限制地啟動服務,并且其后臺服務也可以運行。
處理對用戶可見的任務時,應用將被置于白名單中,例如:
1、處理一條高優先級Firebase 云消息傳遞 (FCM)消息。
2、接收廣播,例如短信/彩信消息
3、從通知執行PendingIntent
在很多情況下,您的應用都可以使用 JobScheduler作業替換后臺服務。 例如,CoolPhotoApp 需要檢查用戶是否已經從朋友那里收到共享的照片,即使該應用未在前臺運行。
使用JobScheduler需要實現一個JobService,那么問題來了,JobService也是一個Service為何它能幸免于難呢?
這就與JobService的工作機制相關,簡單而言,系統進程的JobSchedulerService會綁定到我們自己實現的JobService,根據上文前臺應用的判別,此時我們的應用會被判定為前臺應用。
隱式廣播限制
Android O 的應用無法繼續在其清單中為隱式廣播注冊廣播接收器。 隱式廣播是一種不專門針對該應用的廣播。 例如,ACTION_PACKAGE_REPLACED 就是一種隱式廣播,因為它將發送到注冊的所有偵聽器,讓后者知道設備上的某些軟件包已被替換。
不過,ACTION_MY_PACKAGE_REPLACED 不是隱式廣播,因為不管已為該廣播注冊偵聽器的其他應用有多少,它都會只發送到軟件包已被替換的應用。
應用可以繼續在它們的清單中注冊顯式廣播。
應用可以在運行時使用 Context.registerReceiver() 為任意廣播(不管是隱式還是顯式)注冊接收器。
需要簽名權限的廣播不受此限制所限,因為這些廣播只會發送到使用相同證書簽名的應用,而不是發送到設備上的所有應用。
在許多情況下,之前注冊隱式廣播的應用使用 JobScheduler 作業可以獲得類似的功能。
注:很多隱式廣播當前均已不受此限制所限。 應用可以繼續在其清單中為這些廣播注冊接收器,不管應用針對哪個 API 級別。 有關已豁免廣播的列表,請參閱隱式廣播例外。
注意:
如果目標平臺targetSdk設置為26,靜態注冊的Receiver將收不到隱式廣播,即使這個隱式廣播是應用自身發出的。
如果非要通過靜態注冊的Receiver來接受廣播,則需要為Receiver注冊簽名權限,發送隱式廣播需要使用帶權限的API,如下圖所示:
運行時權限修改
在 Android O 之前,如果應用在運行時請求權限并且被授予該權限,系統會錯誤地將屬于同一權限組并且在清單中注冊的其他權限也一起授予應用。
對于針對 Android O 的應用,此行為已被糾正。系統只會授予應用明確請求的權限。然而,一旦用戶為應用授予某個權限,則所有后續對該權限組中權限的請求都將被自動批準。
例如,假設某個應用在其清單中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。應用請求 READ_EXTERNAL_STORAGE,并且用戶授予了該權限。如果該應用針對的是 API 級別 24 或更低級別,系統還會同時授予 WRITE_EXTERNAL_STORAGE,因為該權限也屬于同一 STORAGE 權限組并且也在清單中注冊過。如果該應用針對的是 Android O,則系統此時僅會授予 READ_EXTERNAL_STORAGE;不過,如果該應用后來又請求 WRITE_EXTERNAL_STORAGE,則系統會立即授予該權限,而不會提示用戶。
關于如何優雅的進行權限的適配,請參考:
Android8.0運行時權限策略變化和適配方案
內容變更通知
Android 8.0 更改了 ContentResolver.notifyChange()和 registerContentObserver(Uri, boolean, ContentObserver)在針對 Android 8.0 的應用中的行為方式。
現在,這些 API 需要在所有 URI 中為頒發機構定義一個有效的 ContentProvider
。使用相關權限定義一個有效的 ContentProvider可幫助您的應用防范來自惡意應用的內容變更,并防止將可能的私密數據泄露給惡意應用。
單看官方文檔介紹,這條是看的云里霧里,經過實踐踩坑后,總結如下:
1.registerContentObserver(Uri, boolean, ContentObserver)與ContentResolver.notifyChange(),在Android O上,若程序需要監聽或者通知的provider申明了權限,而執行程序并沒有相應權限,則會拋出異常,導致程序崩潰。之前的版本,若沒有權限不會拋出異常,只是會注冊或者通知失敗。
以通話記錄與聯系人為例,若示例代碼執行時,程序不具備如下權限:
android.permission.READ_CALL_LOG
android.permission.READ_CONTACTS
則會拋出權限異常,導致程序崩潰。
- 如果你要監聽或者通知的provider不存在,同樣會拋出異常,導致程序崩潰。
應用快捷鍵
Android 8.0 對應用快捷方式做出了以下變更:
com.android.launcher.action.INSTALL_SHORTCUT
廣播不再會對您的應用有任何影響,因為它現在是私有的隱式廣播。相反,您應使用 ShortcutManager類中的 requestPinShortcut函數創建應用快捷方式。
現在,ACTION_CREATE_SHORTCUTIntent 可以創建可使用 ShortcutManager
類進行管理的應用快捷方式。此 Intent 還可以創建不與 ShortcutManager
交互的舊版啟動器快捷方式。在以前,此 Intent 只能創建舊版啟動器快捷方式。
現在,使用 requestPinShortcut()創建的快捷方式和在處理 ACTION_CREATE_SHORTCUT
Intent 的操作組件中創建的快捷方式均已轉換為功能齊全的應用快捷方式。因此,應用現在可以使用 ShortcutManager中的函數來更新這些快捷方式。
舊版快捷方式仍然保留了它們在舊版 Android 中的功能,但您必須在應用中手動將它們轉換成應用快捷方式。
如需了解有關應用快捷方式變更的更多信息,請參閱固定快捷方式和微件預覽功能指南。
小結:
在Android O上,之前舊的創建快捷方式的方法將失效。
在8.0上,AMS直接不會轉發隱式廣播:
com.android.launcher.action.INSTALL_SHORTCUT
即使將targetSdkVersion設置為26以下,創建快捷方式必須使用新的API。
7.1上舊的創建快捷方式的方法依舊有效,但是所以針對非Launcher開發者而言,只需要使用新的API來創建快捷方式即可,對于Launcher開發者,要做的工作就蠻多的了,需要適配ShortcutManger及相關UI。
PS:本文只是針對主要適配點進行闡述,除了上述三點,Android O還有一些其他小細節需要適配如:
1、Android 后臺位置限制(定位APP需要考慮)
2、提醒窗口(使用了SYSTEM_ALERT_WINDOW的應用需要適配)
3、記錄未捕獲的異常
4、隱私性(Android ID生成機制改變,一般需要統計SDK適配)
......
具體細節請參考官方文檔Android O行為變更