又到了我一年一度寫Android適配文章的時間,自己這篇應該會早幾個月發出來,可是前兩三個月主要忙於Flutter的項目,因此這篇文章才姍姍來遲。不過畢竟是9.0的適配,還不算太晚哈!php
國內從去年開始就有消息說,應用上架或者更新要求TargetSdkVersion最低要爲26以上,也就是最低也要適配到8.0。今年來也都逐步地開始落實。好比下圖的小米應用商店公告: html
還包括從8月份開始在Google Play上發佈的應用必須支持64位架構。能夠看到適配工做真的不能像之前同樣爲所欲爲了。好在我以前也有寫過相關的適配攻略,Android適配系列:java
Android 6.0 的動態權限管理android
Android 8.0適配指北apache
進入正題,首先將咱們項目中的targetSdkVersion
改成 28。接下來運行你的項目,看有沒中槍。安全
在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以及各類場景下靈活的配置,詳細的方法能夠查看官方文檔。
在 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"/>
複製代碼
能夠試着搜索一下你的代碼,看是否有調用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" />
複製代碼
在9.0 中,不能直接非 Activity
環境中(好比Service
,Application
)啓動 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);
複製代碼
這類異形屏叫法不少,劉海屏、水滴屏、挖孔屏、美人尖。。。
其實若是你的頁面不須要全屏顯示,那麼不須要額外的適配工做。
若是頁面是全屏顯示(好比啓動頁)。爲了防止你的內容被遮擋,大部分場景下都是可使用獲取狀態欄高度來處理遮擋的適配問題。由於狀態欄的高度都是大於等於劉海的高度。
固然,若是你想利用起來劉海區域,就須要獲取劉海位置等信息進行適配。在Android 9.0中官方提供了DisplayCutout
類,能夠肯定劉海區域的位置,國內的部分廠商在8.0就有了本身的適配方案。
具體的我就不過多介紹了,推薦你們看如下文章:
首先是權限組的變動:
上圖能夠看到,在9.0 中新增權限組CALL_LOG
並將 READ_CALL_LOG
、WRITE_CALL_LOG
和 PROCESS_OUTGOING_CALLS
權限從PHONE
中移入該組。
若是應用須要訪問通話記錄或者須要處理去電,則您必須向 CALL_LOG
權限組明確請求這些權限。 不然會發生 SecurityException
。
PHONE_STATE
Intent 操做讀取電話號碼,同時須要 READ_CALL_LOG
權限和 READ_PHONE_STATE
權限。PhoneStateListener的onCallStateChanged()
中讀取電話號碼,只須要 READ_CALL_LOG
權限。 不須要 READ_PHONE_STATE
權限。在 Android 9 中,調用Build.SERIAL
會始終返回 UNKNOWN
以保護用戶的隱私。若是你的應用須要訪問設備的硬件序列號,那麼須要先請求 READ_PHONE_STATE
權限,而後調用 Build.getSerial()
。
注意非 SDK 接口的限制。主要是一些熱修復、插件化框架涉及比較多,注意及時升級新版本。
多進程使用WebView
注意沒法共用同一數據目錄。 詳細點擊查看
總的來講,9.0的適配工做須要改動和注意的點相比較之前版本的適配來講並很少,從本篇的篇幅就能夠看出來,詳細的變化能夠參看文末的連接。後面若是遇到什麼坑,我也會及時補充進來。感謝你的閱讀!!