Android 9.0 適配指南

又到了我一年一度寫Android適配文章的時間,自己這篇應該會早幾個月發出來,可是前兩三個月主要忙於Flutter的項目,因此這篇文章才姍姍來遲。不過畢竟是9.0的適配,還不算太晚哈!php

1.前言

國內從去年開始就有消息說,應用上架或者更新要求TargetSdkVersion最低要爲26以上,也就是最低也要適配到8.0。今年來也都逐步地開始落實。好比下圖的小米應用商店公告html

在這裏插入圖片描述
固然 Google Play的要求更爲嚴格:
在這裏插入圖片描述

還包括從8月份開始在Google Play上發佈的應用必須支持64位架構。能夠看到適配工做真的不能像之前同樣爲所欲爲了。好在我以前也有寫過相關的適配攻略,Android適配系列:java

2.準備工做

進入正題,首先將咱們項目中的targetSdkVersion改成 28。接下來運行你的項目,看有沒中槍。安全

3.網絡

1.Http請求失敗

在9.0中默認狀況下啓用網絡傳輸層安全協議 (TLS),默認狀況下已停用明文支持。也就是不容許使用http請求,要求使用https。網絡

好比我使用的是okhttp,會報錯:架構

java.net.UnknownServiceException: CLEARTEXT communication to xxxx not permitted by network security policy
複製代碼

解決方法是須要咱們添加網絡安全配置。首先在 res 目錄下新建xml文件夾,添加network_security_config.xml文件:app

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
複製代碼

AndroidManifest.xml中的application添加:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config">
            ...
    </application>
</manifest>
複製代碼

以上這是一種簡單粗暴的配置方法,要麼支持http,要麼不支持http。爲了安全靈活,咱們能夠指定支持的http域名:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
	<!-- Android 9.0 上部分域名時使用 http -->
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example1.com</domain>
    </domain-config>
</network-security-config>
複製代碼

固然不止這些配置,還有抓包配置、設置自定義CA以及各類場景下靈活的配置,詳細的方法能夠查看官方文檔

2.Apache HTTP 客戶端棄用

在 Android 6.0 時,就已經取消了對 Apache HTTP 客戶端的支持。 從 Android 9.0 開始,默認狀況下該庫已從 bootclasspath 中移除。可是耐不住有些SDK中還在使用,好比我見到的友盟QQ分享報錯問題

因此要想繼續使用Apache HTTP,須要在應用的 AndroidManifest.xml 文件中添加:

<uses-library android:name="org.apache.http.legacy" android:required="false"/>
複製代碼

4.前臺服務

能夠試着搜索一下你的代碼,看是否有調用startForegroundService 方法來啓動一個前臺服務。

startForegroundService 主要來源估計都是8.0適配時候加上的:

Intent intentService = new Intent(this, MyService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    startForegroundService(intentService);
} else {
    startService(intentService);
}
複製代碼

9.0 要求建立一個前臺服務須要請求 FOREGROUND_SERVICE 權限,不然系統會引起 SecurityException

java.lang.RuntimeException: Unable to start service com.weilu.test.MyService@81795be with Intent { cmp=com.weilu.test/.MyService }: 
java.lang.SecurityException: Permission Denial: startForeground from pid=28631, uid=10626 requires android.permission.FOREGROUND_SERVICE
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3723)
        at android.app.ActivityThread.access$1700(ActivityThread.java:201)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1705)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:207)
        at android.app.ActivityThread.main(ActivityThread.java:6820)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
複製代碼

解決方法就是AndroidManifest.xml中添加FOREGROUND_SERVICE權限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
複製代碼

5.啓動Activity

在9.0 中,不能直接非 Activity 環境中(好比ServiceApplication)啓動 Activity,不然會崩潰報錯:

java.lang.RuntimeException: Unable to create service com.weilu.test.MyService: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want? at android.app.ActivityThread.handleCreateService(ActivityThread.java:3578) at android.app.ActivityThread.access$1400(ActivityThread.java:201) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1690) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:207) at android.app.ActivityThread.main(ActivityThread.java:6820) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876) 複製代碼

這類問題通常會在點擊推送消息跳轉頁面這類場景,解決方法就是 Intent 中添加標誌FLAG_ACTIVITY_NEW_TASK

Intent intent = new Intent(this, TestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
複製代碼

6.異形屏適配

這類異形屏叫法不少,劉海屏、水滴屏、挖孔屏、美人尖。。。

  1. 其實若是你的頁面不須要全屏顯示,那麼不須要額外的適配工做。

  2. 若是頁面是全屏顯示(好比啓動頁)。爲了防止你的內容被遮擋,大部分場景下都是可使用獲取狀態欄高度來處理遮擋的適配問題。由於狀態欄的高度都是大於等於劉海的高度。

固然,若是你想利用起來劉海區域,就須要獲取劉海位置等信息進行適配。在Android 9.0中官方提供了DisplayCutout 類,能夠肯定劉海區域的位置,國內的部分廠商在8.0就有了本身的適配方案。

具體的我就不過多介紹了,推薦你們看如下文章:

7.權限

首先是權限組的變動:

上圖能夠看到,在9.0 中新增權限組CALL_LOG 並將 READ_CALL_LOGWRITE_CALL_LOGPROCESS_OUTGOING_CALLS 權限從PHONE中移入該組。

1.限制訪問通話記錄

若是應用須要訪問通話記錄或者須要處理去電,則您必須向 CALL_LOG權限組明確請求這些權限。 不然會發生 SecurityException

2.限制訪問電話號碼

  • 要經過 PHONE_STATE Intent 操做讀取電話號碼,同時須要 READ_CALL_LOG 權限和 READ_PHONE_STATE 權限。
  • 要從 PhoneStateListener的onCallStateChanged() 中讀取電話號碼,只須要 READ_CALL_LOG 權限。 不須要 READ_PHONE_STATE 權限。

8.其餘

  • 在 Android 9 中,調用Build.SERIAL 會始終返回 UNKNOWN 以保護用戶的隱私。若是你的應用須要訪問設備的硬件序列號,那麼須要先請求 READ_PHONE_STATE 權限,而後調用 Build.getSerial()

  • 注意非 SDK 接口的限制。主要是一些熱修復、插件化框架涉及比較多,注意及時升級新版本。

  • 多進程使用WebView注意沒法共用同一數據目錄。 詳細點擊查看


總的來講,9.0的適配工做須要改動和注意的點相比較之前版本的適配來講並很少,從本篇的篇幅就能夠看出來,詳細的變化能夠參看文末的連接。後面若是遇到什麼坑,我也會及時補充進來。感謝你的閱讀!!

參考

相關文章
相關標籤/搜索