Android O 適配詳細指南

網易考拉移動端團隊發表於2019-03-04

Android O 適配詳細指南

Android O 適配詳細指南

前言

最近 Google 爸爸對 Google Play 上架的應用提出了目標 API 等級要求

從 2018 年 8 月 1 日起,所有向 Google Play 首次提交的新應用都必須針對 Android 8.0 (API 等級 26) 開發; 2018 年 11 月 1 日起,所有 Google Play 的現有應用更新同樣必須針對 Android 8.0。

Google Play 目標 API 等級(targetSdkVersion)重要變更要求

同時,國內的華為、360、應用寶也要求開發者適配 Android P,否則應用將被不推薦、隱藏甚至下架(華為),可以看出國內應用市場對於推動應用適配新 API 的決心,雖然沒有強制要求適配,但也算國內應用市場的一大進步,相信很快就會有其他應用市場跟進。

為保障華為使用者的使用體驗,華為應用市場已在7月份啟動Android P版本應用適配檢測工作,針對未做適配的應用開發者陸續進行郵件通知。
請您對應用適配這一環節加以重視,並於2018年10月底前完成Android P版本適配工作並自檢通過。針對未適配或在Android P版本體驗欠佳的應用,華為應用市場將在Android P版本機型上採取下架、不推薦更新或遮蔽策略,可能會對您的推廣、使用者口碑及品牌產生影響。

華為公告
360公告
應用寶公告

意義

我們都知道每次 Android 版本的更新都會新增一大波優化功能,比如 Android M 上引入了執行時許可權,Android N 上帶來了 Doze 模式,Android O 上的後臺執行限制

總的來說,新版本會讓我們的 Android 裝置更流暢,更省電,隱私性更好。

應用也可以利用新版本的特性以提升使用者體驗,如 Android M 引入可指定通知欄顏色,以實現完全沉浸式體驗。

所以當然要適配新版本啦

新版本新增了很多優化,但同時對 APP 的限制也更多,比如在新版本上應用保活越來越困難,應用無法在後臺做一些偷偷摸摸的事情,有些應用提供商肯定不樂意啦,因此國內很多應用對 Android API 適配的積極性不高。

現在各大應用市場聯合起來催促開發者適配 Android API,這種情況很定會慢慢有所改觀,國內的 Android 應用生態也會越來越良好。

正文

扯了這麼多,說到底,還是想說為了國內的 Android 應用生態,大家趕緊適配起來新版本吧

俗話說,不能一口吃個胖子,因此我們先從 Android O 介面卡來,也能達到 Google 爸爸的要求

下面是考拉 APP 適配 Android O 的記錄

1. 通知欄

Android 8.0 引入了通知渠道,其允許您為要顯示的每種通知型別建立使用者可自定義的渠道。使用者介面將通知渠道稱之為通知類別。

針對 8.0 的應用,建立通知前需要建立渠道,建立通知時需要傳入 channelId,否則通知將不會顯示。示例程式碼如下:

// 建立通知渠道
private void initNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = mContext.getString(R.string.app_name);
        NotificationChannel channel = new NotificationChannel(mChannelId, name, NotificationManager.IMPORTANCE_DEFAULT);
        mNotificationManager.createNotificationChannel(channel);
    }
}
// 建立通知傳入channelId
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationBarManager.getInstance().getChannelId());
複製程式碼

developer.android.com/about/versi…

2. 後臺執行限制

如果針對 Android 8.0 的應用嘗試在不允許其建立後臺服務的情況下使用 startService() 函式,則該函式將引發一個 IllegalStateException。

我們無法得知系統如何判斷是否允許應用建立後臺服務,所以我們目前只能簡單 try-catch startService(),保證應用不會 crash,示例程式碼:

Intent intent = new Intent(getApplicationContext(), InitializeService.class);
intent.setAction(InitializeService.INITIALIZE_ACTION);
intent.putExtra(InitializeService.EXTRA_APP_INITIALIZE, appInitialize);
ServiceUtils.safeStartService(mApplication, intent);

public static void safeStartService(Context context, Intent intent) {
    try { 
        context.startService(intent);
    } catch (Throwable th) {
        DebugLog.i("service", "start service: " + intent.getComponent() + "error: " + th);
        ExceptionUtils.printExceptionTrace(th);
    }
}
複製程式碼

developer.android.com/about/versi…

3. 允許安裝未知來源應用

針對 8.0 的應用需要在 AndroidManifest.xml 中宣告 REQUEST_INSTALL_PACKAGES 許可權,否則將無法進行應用內升級。

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
複製程式碼

4. 主題的 Activity 設定螢幕方向

針對 8.0 的應用,設定了透明主題的Activity,再設定螢幕方向,程式碼如下:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowIsTranslucent">true</item>
</style>

<activity
    android:name=".MainActivity"
    android:screenOrientation="portrait"
    android:theme="@style/AppTheme">
</activity>
複製程式碼

將會丟擲以下異常:

java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
複製程式碼

大概意思是:只有不透明的全屏Activity可以自主設定介面方向

即使滿足上述條件,該異常也並非一定會出現,為什麼這麼說,看下面兩種表現:

  • targetSdk=26,滿足上述條件,API 26 手機沒問題,API 27 手機沒問題
  • targetSdk=27,滿足上述條件,API 26 手機Crash,API 27 手機沒問題

有點摸不清 Google 的套路了……

可知,targetSdk=26 時,API 26 和 27 都沒有問題,所以這個坑暫時放在適配 API 27 時再填吧。

5. 桌面圖示適配

針對 8.0 的應用如果不適配桌面圖示,則應用圖示在 Launcher 中將會被新增白色背景:

Android O 適配詳細指南

適配方法:一起來學習Android 8.0系統的應用圖示適配吧

適配後的效果:

Android O 適配詳細指南

6. 隱式廣播

由於 Android 8.0 引入了新的廣播接收器限制,因此您應該移除所有為隱式廣播 Intent 註冊的廣播接收器。將它們留在原位並不會在構建時或執行時令應用失效,但當應用執行在 Android 8.0 上時它們不起任何作用。

顯式廣播 Intent(只有您的應用可以響應的 Intent)在 Android 8.0 上仍以相同方式工作。

這個新增限制有一些例外情況。如需檢視在以 Android 8.0 為目標平臺的應用中仍然有效的隱式廣播的列表,請參閱隱式廣播例外。

developer.android.com/about/versi…

我對隱式廣播的理解:

未指定廣播接收器類名,通過 Action 傳送。如有不妥,還請指教。

需要檢查應用靜態註冊的隱式廣播,需要改為動態註冊。

7. 網路連線和 HTTP(S) 連線

Android 8.0 對網路連線和 HTTP(S) 連線行為做出了以下變更:

  • 無正文的 OPTIONS 請求具有 Content-Length: 0 標頭。之前,這些請求沒有 Content-Length 標頭。

  • HttpURLConnection 在包含斜線的主機或頒發機構名稱後面附加一條斜線,使包含空路徑的網址規範化。例如,它將 example.com 轉化為 example.com/。

  • 通過 ProxySelector.setDefault() 設定的自定義代理選擇器僅針對所請求的網址(架構、主機和埠)。因此,僅可根據這些值選擇代理。傳遞至自定義代理選擇器的網址不包含所請求的網址的路徑、查詢引數或片段。

  • URI 不能包含空白標籤。
    之前,平臺支援一種權宜方法,即允許主機名稱中包含空白標籤,但這是對 URI 的非法使用。此權宜方法只是為了確保與舊版 libcore 相容。開發者如果對 API 使用不當,將會看到一條 ADB 訊息:“URI example..com 的主機名包含空白標籤。此格式不正確,將不被未來的 Android 版本所接受。”Android 8.0 廢除了此權宜方法;系統對格式錯誤的 URI 會返回 null。

  • Android 8.0 在實現 HttpsURLConnection 時不會執行不安全的 TLS/SSL 協議版本回退。

  • 對隧道 HTTP(S) 連線處理進行了如下變更:
    在通過連線建立隧道 HTTP(S) 連線時,系統會在 Host 行中正確放置埠號 (:443) 並將此資訊傳送至中間伺服器。之前,埠號僅出現在 CONNECT 行中。
    系統不再將隧道連線請求中的 user-agent 和 proxy-authorization 標頭髮送至代理伺服器。
    在建立隧道時,系統不再將隧道 Http(s)URLConnection 中的 proxy-authorization 標頭髮送至代理。相反,由系統生成 proxy-authorization 標頭,在代理響應初始請求傳送 HTTP 407 後將其傳送至此代理。

同樣地,系統不再將 user-agent 標頭由隧道連線請求複製到建立隧道的代理請求。相反,庫為此請求生成 user-agent 標頭。

  • 如果之前執行的 connect() 函式失敗,send(java.net.DatagramPacket) 函式將會引發 SocketException。
    如果存在內部錯誤,DatagramSocket.connect() 會引發 pendingSocketException。對於 Android 8.0 之前的版本,即使 send() 呼叫成功,後續的 recv() 呼叫也會引發 SocketException。為確保一致性,現在這兩個呼叫均會引發 > SocketException。
  • 在回退到 TCP Echo 協議之前,InetAddress.isReachable() 會嘗試執行 ICMP。
    對於某些遮蔽埠 7 (TCP Echo) 的主機(例如 google.com),如果它們接受 ICMP Echo 協議,現在也許能夠訪問它們。
    對於確實無法訪問的主機,此項變更意味著呼叫需要兩倍的時間才能返回結果。

developer.android.com/about/versi…

這點應用一般無需適配

8. 檢視焦點

可點選的 View 物件現在預設也可以成為焦點。如果您希望 View 物件可點選但不可成為焦點,請在包含 View 的佈局 XML 檔案中將 android:focusable 屬性設定為 false,或者將 false 傳遞至應用介面邏輯中的 setFocusable()。

developer.android.com/about/versi…

這點基本無需適配

9. 許可權

在 Android 8.0 之前,如果應用在執行時請求許可權並且被授予該許可權,系統會錯誤地將屬於同一許可權組並且在清單中註冊的其他許可權也一起授予應用。

對於針對 Android 8.0 的應用,此行為已被糾正。系統只會授予應用明確請求的許可權。然而,一旦使用者為應用授予某個許可權,則所有後續對該許可權組中許可權的請求都將被自動批准。

例如,假設某個應用在其清單中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。應用請求 READ_EXTERNAL_STORAGE,並且使用者授予了該許可權。如果該應用針對的是 API 級別 24 或更低階別,系統還會同時授予 > WRITE_EXTERNAL_STORAGE,因為該許可權也屬於同一 STORAGE 許可權組並且也在清單中註冊過。如果該應用針對的是 Android 8.0,則系統此時僅會授予 READ_EXTERNAL_STORAGE;不過,如果該應用後來又請求 > WRITE_EXTERNAL_STORAGE,則系統會立即授予該許可權,而不會提示使用者。

developer.android.com/about/versi…

考拉中的許可權都是按需申請的,不需要修改。

10. Tinker

特別是在Android N之後,由於混合編譯的inline策略修改,對於市面上的各種方案都不太容易解決。而Tinker熱補丁方案不僅支援類、So以及資源的替換,它還是2.X-8.X(1.9.0以上支援8.X)的全平臺支援。

github.com/Tencent/tin…

經測試,Tinker在8.0上功能正常。

總結

隨著 Android 版本的不斷迭代,Android 系統的體驗已經越來越好了。

Android 系統的絢爛多彩離不開廣大的 Android 開發者,我們作為開發者,我們應該儘快適配 Android 新版本,讓我們的應用擁有最好的體驗。

本篇介紹了 Android O 的適配要點,如有不足,還請指教

下一篇將會介紹 Android P 的適配,敬請期待

參考:

Android 綠色應用公約

統一推送聯盟

相關文章