此限制不只僅侷限於sdk層 ( 直接引用 或者 反射 ),而觸及到了 JNI 層,其實早在 android N 的時候就限制了 C / C++ 使用的符號集合,一旦NDK有沒法通知的變動,毫無疑問會引發程序的 crash 。html
SDK 接口指在 Android 框架 軟件包索 中記錄的接口, Google爲了讓開發者有過渡的時間而且起到警示的做用, 針對 non-sdk 接口設定了不一樣級別的名單類型:前端
咱們平時開發須要注意的也就是 深灰名單 和 黑名單,不用太在乎 淺灰名單 ,由於前面說到過能夠直接引用 non-sdk 接口,這裏基本上是指直接引用 淺灰名單的接口。java
這邊先舉個例子:在官方的淺灰名單中,其中列舉可不少咱們平時用的接口,例如 Intent 獲取資源:linux
Landroid/content/Intent;->FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT:I
Landroid/content/Intent;->getExtra(Ljava/lang/String;)Ljava/lang/Object;
Landroid/content/Intent;->getExtra(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
複製代碼
然而這正是咱們平時用的不少的 Api,因此沒必要太在乎,淺灰名單屬於很是低級別的警告, 客戶端適配迫切須要解決的是 黑名單 的列表: 黑名單 基本上是咱們平時應用級開發不會用到的Api,例如:android
Lsun/util/calendar/BaseCalendar;->getMonthLength(II)I
複製代碼
對應sdk中代碼:web
//推薦使用的方法,官方不限制
public int getMonthLength(CalendarDate var1) {
BaseCalendar.Date var2 = (BaseCalendar.Date)var1;
int var3 = var2.getMonth();
if (var3 >= 1 && var3 <= 12) {
return this.getMonthLength(var2.getNormalizedYear(), var3);
} else {
throw new IllegalArgumentException("Illegal month value: " + var3);
}
}
//官方黑名單方法,運行在 P 設備上直接crash
private int getMonthLength(int var1, int var2) {
int var3 = DAYS_IN_MONTH[var2];
if (var2 == 2 && this.isLeapYear(var1)) {
++var3;
}
return var3;
}
複製代碼
因此呢,黑名單 雖然聽起來 駭人聽聞,可是對於存量app的影響倒不是很大,由於基本上都是一些私有的,罕見的方法。算法
影響範圍最大的當屬 深灰名單, 由於官方強烈不推薦使用,可是爲了給開發者緩衝時間,只有 Target Api 28+ 纔會出現異常,表明性的 Api 有 DexFile 類:shell
Ldalvik/system/DexFile;-><init>(Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;-><init>(Ljava/lang/String;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;-><init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;-><init>(Ljava/nio/ByteBuffer;)V
Ldalvik/system/DexFile;->DEX2OAT_FOR_BOOT_IMAGE:I
Ldalvik/system/DexFile;->DEX2OAT_FOR_FILTER:I
Ldalvik/system/DexFile;->DEX2OAT_FOR_RELOCATION:I
Ldalvik/system/DexFile;->DEX2OAT_FROM_SCRATCH:I
Ldalvik/system/DexFile;->NO_DEXOPT_NEEDED:I
Ldalvik/system/DexFile;->closeDexFile(Ljava/lang/Object;)Z
Ldalvik/system/DexFile;->createCookieWithArray([BII)Ljava/lang/Object;
Ldalvik/system/DexFile;->createCookieWithDirectBuffer(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;
Ldalvik/system/DexFile;->defineClass(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;Ldalvik/system/DexFile;Ljava/util/List;)Ljava/lang/Class;
Ldalvik/system/DexFile;->defineClassNative(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;Ldalvik/system/DexFile;)Ljava/lang/Class;
複製代碼
以及 AssetManager 相關:apache
Landroid/content/res/AssetManager;->TAG:Ljava/lang/String;
Landroid/content/res/AssetManager;->addAssetPathInternal(Ljava/lang/String;Z)I
Landroid/content/res/AssetManager;->addAssetPathNative(Ljava/lang/String;Z)I
Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I
Landroid/content/res/AssetManager;->addOverlayPathNative(Ljava/lang/String;)I
Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V
複製代碼
這兩個類被列入深灰名單基本上就打翻了市面上一衆 熱修復 框架, 這意味着若是您的App 是以 28+爲目標版本,而且運行在 android P 之上,則這些熱修復框架可能沒法正常運行。api
在今年6月份 GMTC(全球大前端技術大會) 的時候,京東架構師發表了演講 《當插件化趕上android P》 中就提到了,去黑科技化,目前 Android P 的 non-sdk 限制已經影響到京東的 插件框架。
有兩種方法:
若是您的本地有 AOSP 項目的話,在根目錄運行
make hiddenapi-aosp-blacklist
複製代碼
而後,能夠在如下位置找到文件:
out/target/common/obj/PACKAGING/hiddenapi-aosp-blacklist.txt
複製代碼
你大能夠在 相應名單中 查找你想查找的類,不過官方提供了自動掃描工具 veridex。
下載到本地目錄, 找到你的系統對應的腳本目錄:
使用命令掃描:
appcompat.sh --dex-file=apk路徑
複製代碼
能夠看到咱們的項目中只有一個 深灰名單的警告。
下圖是利用各類途徑使用 non-sdk 接口的結果:
訪問方式 | 結果 |
---|---|
Dalvik 指令引用字段 | 引起 NoSuchFieldError |
Dalvik 指令引用函數 | 引起 NoSuchMethodError |
經過 Class.getDeclaredField() 或 Class.getField() 反射 | 引起 NoSuchFieldException |
經過 Class.getDeclaredMethod() 或 Class.getMethod() 反射 | 引起 NoSuchMethodException |
經過 Class.getDeclaredFields() 或 Class.getFields() 反射 | 結果中未出現非 SDK 成員 |
經過 Class.getDeclaredMethods() 或 Class.getMethods(). | 反射結果中未出現非 SDK 成員. |
經過 env->GetFieldID() 調用 JNI | 返回 NULL,引起 NoSuchFieldError |
經過 env->GetMethodID() 調用 JNI | 返回 NULL,引起 NoSuchMethodError |
相似寫法,將會發生 NoSuchProviderException:
SecureRandom.getInstance("SHA1PRNG", "Crypto")
複製代碼
在android P 以前的設備上,使用 Crypto 提供商,若是 target < 24 (N) 可以正常使用,若是target 24+ 則會失敗
在adnrodi P 設備上 因爲完全移除了 Crypto, 所以不管 target 是何值 都會拋出異常 NoSuchProviderException
加密功能的 BC 提供者被移除,官方博客中這樣說:
Starting in Android P, we plan to deprecate some functionality from the BC provider that's duplicated by the AndroidOpenSSL (also known as Conscrypt) provider
android P開始,BC 提供者變成不推薦,若是targetApi < P 會有日誌警告 targetApi >=p (28+) 將會拋出 NoSuchAlgorithmException ,如下寫法將會受影響:
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC") or
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))
複製代碼
所以,建議不要再指定 provider 而使用默認實現。
若是您的應用須要在運行 Android 9 的設備上檢測傳感器事件,請使用前臺服務。
Android P 引入 CALL_LOG 權限組並將 READ_CALL_LOG、WRITE_CALL_LOG 和 PROCESS_OUTGOING_CALLS 權限移入該組。 在以前的 Android 版本中,這些權限位於 PHONE 權限組
Android 8.0和Android 8.1:
成功調用 WifiManager.getScanResults() 須要如下任何一項權限:
若是調用應用程序沒有任何這些權限,則調用將失敗並顯示 SecurityException。
Android 9及更高版本:
成功調用 WifiManager.startScan() 須要知足如下全部條件:
成功調用 WifiManager.getScanResults() 須要知足如下全部條件:
若是調用應用程序不知足全部這些要求,則調用將失敗並顯示 SecurityException。
相似的限制也適用於 getConnectionInfo() 函數,該函數返回描述當前 Wi-Fi 鏈接的 WifiInfo 對象。 若是調用應用具備如下權限,則只能使用該對象的函數來檢索 SSID 和 BSSID 值:
在 RFC 2818
中,回退到 CN 已被棄用。所以,Android 再也不回退到使用 CN。 要驗證主機名,服務器必須出示具備匹配 SAN 的證書。 不包含與主機名匹配的 SAN 的證書再也不被信任
其實,自 Android 6 發佈,就移除了對 Apache HTTP 客戶端的支持,而推薦改用 HttpURLConnection 類,由於它能夠經過透明壓縮和響應緩存減小網絡使用,並可最大限度下降耗電量, 今後咱們變習慣這樣使用 Apache HTTP API,即在 build.geadle
添加:
android {
useLibrary 'org.apache.http.legacy'
}
複製代碼
androd P 開始,默認狀況下該內容庫已從 bootclasspath 中移除且不可用於應用。
這句話怎麼理解,也就是說默認 Apache HTTP API 不可用,即便在build.geadle
申明瞭該庫。 這種說法分兩種狀況: 運行在 android P 設備上的應用:
Target 28
,默認會報 NoClassDefFoundError,由於此庫被禁止使用,要繼續使用 Apache HTTP 客戶端,以 Android 9 及更高版本爲目標的應用能夠向其 AndroidManifest.xml
添加如下內容:
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
複製代碼
Target < 28
能夠和 android 6.0
一致。
bootclasspath 是 linux 系統變量,是系統在啓動時會預先加載的類,以提升系統性能,這是 小米 MIX(7.0)
上的 bootclasspath 變量:
/system/bin/sh: /system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/apache-xml.jar:/system/framework/org.apache.http.legacy.boot.jar:/system/framework/vivo-framework.jar:/system/framework/tcmiface.jar:/system/framework/telephony-ext.jar:/system/framework/vivo-media.jar:/system/framework/qcrilhook.jar:/system/framework/WfdCommon.jar:/system/framework/com.qti.location.sdk.jar:/system/framework/oem-services.jar:/system/framework/qcom.fmradio.jar: not found
複製代碼
變量中有:/system/framework/org.apache.http.legacy.boot.jar
,所以系統會幫咱們加載,默認容許使用。
這是 android P
上的 bootclasspath 變量:
/system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/bouncycastle.jar:/system/framework/apache-xml.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/android.hidl.base-V1.0-java.jar:/system/framework/android.hidl.manager-V1.0-java.jar:/system/framework/framework-oahl-backward-compatibility.jar:/system/framework/android.test.base.jar
generic_x86_64:/ $
複製代碼
是沒有 apache 的 http 庫的, 可是 他們有一個共同特色,就是系統內置了 apache 包, 在 /system/framework/
目錄下:
,可是我有一個困惑的地方,就是 一樣 是運行在 android P 設備上 和 運行在 低版本上(>M) DexPathList
值確不同:
android P
:PathClassLoader// 這是httpClient的 ClassLoader
DexPathList[[zip file "/system/framework/org.apache.http.legacy.boot.jar",
zip file "/data/app/com.example.leixiang.demoapp-hOOUC7E0LuRvgmYC38vd5w==/base.apk"
],nativeLibraryDirectories=[/data/app/com.example.leixiang.demoapp-hOOUC7E0LuRvgmYC38vd5w==/lib/x86_64, /system/lib64]]
複製代碼
android N
:dalvik.system.PathClassLoader //這是httpClient的 ClassLoader
[DexPathList[[zip file "/data/app/com.example.leixiang.demoapp-1/base.apk"
],nativeLibraryDirectories=[/data/app/com.example.leixiang.demoapp-1/lib/arm64, /system/lib64, /vendor/lib64]]]
複製代碼
他們不一樣之處在於,android N 設備上的 DexPathList
裏面多了 apache
的包,可是他們的加載器卻都仍是 PathClassLoader
,我想多是 P 系統上再也不預先加載 apache http 相關類,因此把他加入 DexPathList
? , 而且 P 以前的系統加載 bootclasspath 中類也是用的 PathClassLoader
?這個有待研究。
Android 9 引入了一項新的電池管理功能,即應用待機羣組。 應用待機羣組能夠基於應用最近使用時間和使用頻率,幫助系統排定應用請求資源的優先級。 根據使用模式,每一個應用都會歸類到 五個 優先級羣組之一中。 系統將根據應用所屬的羣組限制每一個應用能夠訪問的設備資源:
若是用戶當前正在使用應用,應用將被歸到「活躍」羣組中,例如:
FCM是指google推送啦,國內不要想了,至於長鏈接和心跳包是否會限制要看國內廠商具體操做了。
若是應用常常運行,但當前未處於活躍狀態,它將被歸到「工做集」羣組中。 例如,用戶在大部分時間都啓動的某個社交媒體應用可能就屬於「工做集」羣組。 若是應用被間接使用,它們也會被升級到「工做集」羣組中 。
若是應用會按期使用,但不是天天都必須使用,它將被歸到「經常使用」羣組中。 例如,用戶在健身房運行的某個鍛鍊跟蹤應用可能就屬於「經常使用」羣組。
若是應用不常用,那麼它屬於「極少使用」羣組。 例如,用戶僅在入住酒店期間運行的酒店應用就可能屬於「極少使用」羣組。
安裝可是從未運行過的應用會被歸到「從未使用」羣組中。 系統會對這些應用施加極強的限制。
咱們能夠利用 UsageStatsManager.getAppStandbyBucket() 查看咱們處於哪個分組,此 api 是 21 添加。
不過用戶能夠經過配置 低電耗 白名單來擺脫分組的限制,具體配置方法看這裏。
如下是各分組對應的活動限制:
Setting | Jobs * | Alarms | Network | Firebase Cloud Messaging § |
---|---|---|---|---|
User Restricts Background Activity | ||||
Restrictions enabled: | Never | Never | Never | No restriction |
Doze | ||||
Doze active: | Deferred to window | Regular alarms: Deferred to window While-idle alarms: Deferred up to 9 minutes | Deferred to window | High priority: No restriction Normal priority: Deferred to window |
App Standby Buckets (by bucket) | ||||
Active: | No restriction | No restriction | No restriction | No restriction |
Working set: | Deferred up to 2 hours | Deferred up to 6 minute | No restriction | No restriction |
Frequent: | Deferred up to 8 hours | Deferred up to 30 minutes | No restriction | High priority: 10/day |
Rare: | Deferred up to 24 hours | Deferred up to 2 hours | Deferred up to 24 hours | High priority: 5/day |
咱們能夠經過 adb命令 讓咱們的調試設備處於特定分組來測試相關的行爲。
$ adb shell am set-standby-bucket packagename active|working_set|frequent|rare
複製代碼
前臺服務 可讓你應用處於活躍狀態,上面提到過 前臺服務 可讓你的應用分組處於 活躍分組。
Target 28+ 並使用前臺服務的應用必須請求 FOREGROUND_SERVICE
權限。 這是 普通權限,所以,系統會自動爲請求權限的應用授予此權限。
在 Android 9 中,Build.SERIAL
始終設置爲 "UNKNOWN"
以保護用戶的隱私。
若是您的應用須要訪問設備的硬件序列號,您應改成請求 READ_PHONE_STATE
權限,而後調用 getSerial()。
android P SDK
api:
/**
* A hardware serial number, if available. Alphanumeric only, case-insensitive.
* For apps targeting SDK higher than {@link Build.VERSION_CODES#O_MR1} this
* field is set to {@link Build#UNKNOWN}.
*
* @deprecated Use {@link #getSerial()} instead.
**/
@Deprecated
// IMPORTANT: This field should be initialized via a function call to
// prevent its value being inlined in the app during compilation because
// we will later set it to the value based on the app's target SDK. public static final String SERIAL = getString("no.such.thing"); 複製代碼
以 Android 9 爲目標平臺的應用應採用私有 DNS API
。 具體而言,當系統解析程序正在執行 DNS-over-TLS
時,應用應確保任何內置 DNS 客戶端均使用加密的 DNS 查找與系統相同的主機名,或停用它而改用系統解析程序。 停用路徑: Settings -> private dns
:
若是您的應用 Target 28+,則默認狀況下 isCleartextTrafficPermitted() 函數返回 false。 若是您的應用須要爲特定域名啓用明文,您必須在應用的網絡安全性配置中針對這些域名將 cleartextTrafficPermitted
顯式設置爲 true
。
否定會出現如下日誌錯誤輸出:
W/Glide: Load failed for http://xxx-99billxx.ufile.ucloud.cn/online/image/A1707101417703-349-0xxhdpi
java.io.IOException: Cleartext HTTP traffic to xxx-99billxx.ufile.ucloud.cn not permitted
複製代碼
注意:該api是 api 23 才引入的。 因此,因爲一些歷史緣由沒法及時把服務器變動爲 https 的應用,應該經過配置文件針對特定域名容許使用明文傳輸,也就是 http 服務。
定義配置文件 res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">secure.example.com</domain>
</domain-config>
</network-security-config>
複製代碼
而後在manifest.xml 中申明
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
複製代碼
爲改善 Android 9 中的應用穩定性和數據完整性,應用沒法再讓多個進程共用同一 WebView 數據目錄。 此類數據目錄通常存儲 Cookie、HTTP 緩存以及其餘與網絡瀏覽有關的持久性和臨時性存儲
若是您的應用必須在多個進程中使用 WebView 的實例,則必須先利用 WebView.setDataDirectorySuffix() 函數爲每一個進程指定惟一的數據目錄後綴,而後再在該進程中使用 WebView 的給定實例。 該函數會將每一個進程的網絡數據放入其在應用數據目錄內本身的目錄中。
注:即便您使用 setDataDirectorySuffix(),系統也不會跨應用的進程界限共享 Cookie 以及其餘網絡數據。 若是應用中的多個進程須要訪問同一網絡數據,您須要自行在這些進程之間複製數據。 例如,您能夠調用 getCookie() 和 setCookie(),在不一樣進程之間手動傳輸 Cookie 數
改進了 dexer 以及協調Jetbranins改進 Kotlin Compier 使用 kotlin 編寫的App在android P 上運行得更快,而且和 Jetbrains 團隊溝通,提供了新的 kotlin 插件。
替代 BitmapFactory 能夠從 流、file、byte buffer、 uRL 加載 Bitmap 和 Drawable 支持精確尺寸縮放, 而且支持加載 gif 、 Webp, 以及圓角等樣式設置。
在室內,IEEE 802.11 MC WI-FI protocol 測量與附近wifi 連接點的距離(2~3個),經過 RTT,來測量距離, 能精確到 1~2米 在提供硬件支持的 Android P 設備上,應用可使用全新的 RTT API 來測量與附近支持 RTT 的 Wi-Fi 接入點 (AP) 的距離,設備不須要鏈接至 AP 便可使用 RTT
使用uses-feature來標註:
<uses-feature android:name="android.hardware.wifi.rtt" />
複製代碼
顯示文本可能很複雜,包含多種字體,行間距,字母間距,文本方向,換行符,連字符等功能。TextView必須作不少工做來測量和佈置給定的文本:讀取字體文件,查找字形,肯定形狀,測量邊界框以及在內部字緩存中緩存單詞。更重要的是,全部這些工做都發生在 UI線程 上,它可能會致使您的應用程序 丟幀 測量文本可能佔用設置文本所需時間的 90%
android P 正式引入, 對於 android P 以前經過 Jetpack 的PrecomputedTextCompat使用.
// UI thread
val params: PrecomputedText.Params = textView.getTextMetricsParams()
val ref = WeakReference(textView)
executor.execute {
// background thread
val text = PrecomputedText.create("Hello", params)
val textView = ref.get()
textView?.post {
// UI thread
val textViewRef = ref.get()
textViewRef?.text = text
}
}
複製代碼
android P 引入了文本放大鏡,以改善用戶選擇文本的體驗。放大鏡經過能夠在文本上拖動的窗格查看放大文本,幫助用戶精肯定位光標或文本選擇手柄,只須要覆寫視圖的 **OnTouchEvent()**方法:
fun onTouchEvent(event:MotionEvent):Boolean {
when(event.actionMasked){
MotionEvent.ACTION_DOWN - >
magnifier.show(event.x,event.y)
MotionEvent.ACTION_MOVE - >
magnifier.show(event.x,event.y)
MotionEvent.ACTION_UP - >
magnifier.dismiss()
}
}
複製代碼
可是惋惜的是:目前低版本設備(<P)沒有相關兼容類使用。
在運行 Android 9 或更高版本的設備上,Android 運行時 (ART) 提早編譯器經過將應用軟件包中的 DEX 文件轉換爲更緊湊的表示形式,進一步優化了壓縮的 Dalvik Executable 格式 (DEX) 文件。 此項變動可以讓您的應用啓動更快並消耗更少的磁盤空間和內存。
這種改進特別有利於磁盤 I/O 速度較慢的低端設備。