本文主要是從官方文檔中篩選出一些常見的適配項,如有任何紕漏或須要補充的,歡迎你們在評論區指出。java
Android 9.0 中限制了 HTTP(明文傳輸)網絡請求,若仍繼續使用HTTP請求,則會在日誌中提示如下異常(只是沒法正常發出請求,不會致使應用崩潰):android
java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy
適配的方法以下:面試
在資源目錄中新建一個 xml 文件,例如 xml/network_security_config.xml,而後在文件中填寫如下內容:apache
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
在AndroidManifest.xml進行配置:canvas
<application ... android:networkSecurityConfig="@xml/network_security_config"> ... </application>
因爲官方在 Android 9.0 中移除了全部 Apache HTTP Client 相關的類,所以咱們的應用或是一些第三方庫若是使用了這些類,就會拋出找不到類的異常:緩存
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/conn/scheme/SchemeRegistry;
若須要繼續使用 Apache HTTP Client ,可經過如下方法進行適配:性能優化
在 AndroidManifest.xml 中添加如下內容:網絡
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
或者在應用中直接將 Apache HTTP Client 相關的類打包並進行引用架構
一直以來,官方提供的接口分爲了 SDK 接口和非 SDK 接口。SDK 接口即官方支持的接口,開發者能夠直接調用不會有任何限制。通常而言,SDK 接口都記錄在官方的接口索引中,沒有記錄的就視爲非 SDK 接口,例如一些使用了 @hide 標註的方法。app
以往開發者對於非 SDK 接口的調用一般是利用反射或者JNI間接調用的方式進行,但這樣的調用方式若是處理不當會比較容易出現一些未知的錯誤。爲了提高用戶體驗和下降應用發生崩潰的風險,Android 9.0 對應用能使用的非 SDK 接口實施了限制,具體的限制手段請見下表:
此外,爲了開發者可以順利過渡到 Android 9.0,官方對非 SDK 接口進行了分類,共分爲三類,light-greylist(淺灰名單)、dark-greylist(深灰名單)以及blacklist(黑名單):
能夠經過如下方式進行測試(詳情請至官方文檔):
建議使用第三種方式,該工具的掃描結果會列出應用對於三個限制名單中的接口的調用細節。
在 Android 9.0 中,應用在使用前臺服務以前必須先申請 FOREGROUND_SERVICE 權限,不然就會拋出 SecurityException 異常。
此外,因爲 FOREGROUND_SERVICE 權限只是普通權限,所以開發者只需在 AndroidManifest.xml 中註冊此權限便可,系統會自動對此權限進行受權:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
在 Android 7.0(API 級別 24)以前,若開發者須要經過非 Activity context 啓動 Activity,就必須設置 Intent 標誌 FLAG_ACTIVITY_NEW_TASK,不然會啓動失敗並拋出如下異常
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
但這個要求在更新 Android 7.0 之後因爲系統問題被臨時取消了,開發者即便不設置標誌也能夠正常啓動 Activity。而在 Android 9.0 中官方修復了這個問題,這個要求從新開始強制執行,所以開發者在適配 Android 9.0 時須要注意這個問題。
Android 9.0 中爲了改善應用穩定性和數據完整性,應用沒法再讓多個進程共用同一 WebView 數據目錄。此類數據目錄通常存儲 Cookie、HTTP 緩存以及其餘與網絡瀏覽有關的持久性和臨時性存儲。
若是開發者須要在多進程中使用 WebView,則必須先調用 WebView.setDataDirectorySuffix() 方法爲每一個進程設置用於存儲 WebView 數據的目錄。若多進程 WebView 之間須要共享數據,開發者需本身經過 IPC 的方式實現。
此外,若開發者只想在一個進程中使用 WebView,而且但願嚴格執行這個規則,能夠經過在其餘進程中調用 WebView.disableWebView() 方法,這樣其餘進程建立 WebView 實例就會拋出異常。
Android 9.0 中若是在使用繪圖裁剪功能時設置了除 Region.Op.INTERSECT 或 Region.Op.DIFFERENCE 之外的類型,就會拋出如下異常:
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
具體緣由是官方廢棄了那幾個具備 Region.Op 參數的裁剪方法,如 clipRect(@NonNull RectF rect, @NonNull Region.Op op) :
/** * Modify the current clip with the specified rectangle. * * @param rect The rect to intersect with the current clip * @param op How the clip is modified * @return true if the resulting clip is non-empty * * @deprecated Region.Op values other than {@link Region.Op#INTERSECT} and * {@link Region.Op#DIFFERENCE} have the ability to expand the clip. The canvas clipping APIs * are intended to only expand the clip as a result of a restore operation. This enables a view * parent to clip a canvas to clearly define the maximal drawing area of its children. The * recommended alternative calls are {@link #clipRect(RectF)} and {@link #clipOutRect(RectF)}; * * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters. */ @Deprecated public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) { checkValidClipOp(op); return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt); } private static void checkValidClipOp(@NonNull Region.Op op) { if (sCompatiblityVersion >= Build.VERSION_CODES.P && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) { throw new IllegalArgumentException( "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed"); } }
對於這個問題,能夠經過如下方法進行適配:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { canvas.clipPath(path); } else { canvas.clipPath(path, Region.Op.XOR);// REPLACE、UNION 等類型 }
Android 9.0 以前,開發者可使用 Build.SERIAL 獲取設備的序列號。如今這個方法被棄用了,Build.SERIAL 將始終設置爲 "UNKNOWN" 以保護用戶的隱私。
適配的方法爲先請求 READ_PHONE_STATE 權限,而後調用 Build.getSerial() 方法。