Android P 適配指南

王超1478507995000發表於2018-08-31

前言

Goolge自 android L (6.0) 以來就持續對安裝系統進行 安全 以及 效能上的升級,此次的 android P (9.0)也不例外, 更大程度上對之前一些版本一些警告的具體落實,無論你的 Target Api 是否是 28 都將受到影響。

1. 影響所有應用 (無論TargetApi 是否 28+)

1.1 non-sdk 介面限制

此限制不僅僅侷限於sdk層 ( 直接引用 或者 反射 ),而觸及到了 JNI 層,其實早在 android N 的時候就限制了 C / C++ 使用的符號集合,一旦NDK有無法通知的變更,毫無疑問會引起程式的 crash 。

1.1.1 SDK 介面和非 SDK 介面

SDK 介面指在 Android 框架 軟體包索 中記錄的介面, Google為了讓開發者有過渡的時間並且起到警示的作用, 針對 non-sdk 介面設定了不同級別的名單型別:

  • 白名單:SDK
  • 淺灰名單:仍可以訪問的非 SDK 函式/欄位。
  • 深灰名單: * 對於 Target Api SDK 低於 API 28 的應用,允許使用深灰名單介面。
    • 對於 Target Api SDK 為 API 28 或更高階別的應用:行為與黑名單相同。
  • 黑名單:無論 Target Api SDK 如何。 平臺將表現為似乎介面並不存在。 例如,無論應用何時嘗試使 用介面,平臺都會引發 NoSuchMethodError/NoSuchFieldException。

我們平時開發需要注意的也就是 深灰名單黑名單,不用太在意 淺灰名單 ,因為前面說到過可以直接引用 non-sdk 介面,這裡基本上是指直接引用 淺灰名單的介面

這邊先舉個例子:在官方的淺灰名單中,其中列舉可很多我們平時用的介面,例如 Intent 獲取資源:

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,例如:

	Lsun/util/calendar/BaseCalendar;->getMonthLength(II)I
複製程式碼

對應sdk中程式碼:

    //推薦使用的方法,官方不限制
    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 類:

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 相關:

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 之上,則這些熱修復框架可能無法正常執行

在今年6月份 GMTC(全球大前端技術大會) 的時候,京東架構師發表了演講 《當外掛化遇上android P》 中就提到了,去黑科技化,目前 Android P 的 non-sdk 限制已經影響到京東的 外掛框架

1.1.2 獲取相關 non-sdk 名單

有兩種方法:

  • 如果您的本地有 AOSP 專案的話,在根目錄執行

       make hiddenapi-aosp-blacklist
    複製程式碼

然後,可以在以下位置找到檔案:

out/target/common/obj/PACKAGING/hiddenapi-aosp-blacklist.txt
複製程式碼
  • 另外更好的辦法是直接用官方編譯好的結果,在 這裡獲取 檢視。

1.1.3 檢查專案中的 non-sdk

你大可以在 相應名單中 查詢你想查詢的類,不過官方提供了自動掃描工具 veridex

下載到本地目錄, 找到你的系統對應的指令碼目錄:

Android P 適配指南
解壓目錄

使用命令掃描:

 appcompat.sh --dex-file=apk路徑
複製程式碼

Android P 適配指南

可以看到我們的專案中只有一個 深灰名單的警告。

1.1.4 呼叫non-sdk 介面

下圖是利用各種途徑使用 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

1.2 安全相關

1.2.1 加密變更

Crypto Java 加密架構 (JCA) 提供程式現已被移除

類似寫法,將會發生 NoSuchProviderException:

	SecureRandom.getInstance("SHA1PRNG", "Crypto")
複製程式碼

android P 之前的裝置上,使用 Crypto 提供商,如果 target < 24 (N) 能夠正常使用,如果target 24+ 則會失敗

adnrodi P 裝置上 由於徹底移除了 Crypto, 因此無論 target 是何值 都會丟擲異常 NoSuchProviderException

許多演算法的 Bouncy Castle 版本被棄用

加密功能的 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 而使用預設實現。

1.3 隱私相關

後臺應用訪問受限

  • 您的應用不能訪問麥克風或攝像頭。
  • 使用連續報告模式的感測器(例如加速度計和陀螺儀)不會接收事件。
  • 使用變化或一次性報告模式的感測器不會接收事件。

如果您的應用需要在執行 Android 9 的裝置上檢測感測器事件,請使用前臺服務

許可權組變更

Android P 引入 CALL_LOG 許可權組並將 READ_CALL_LOGWRITE_CALL_LOGPROCESS_OUTGOING_CALLS 許可權移入該組。 在之前的 Android 版本中,這些許可權位於 PHONE 許可權組

wifi掃描許可權變更:

Android 8.0和Android 8.1:

成功呼叫 WifiManager.getScanResults() 需要以下任何一項許可權:

  • ACCESS_FINE_LOCATION
  • ACCESS_COARSE_LOCATION
  • CHANGE_WIFI_STATE

如果呼叫應用程式沒有任何這些許可權,則呼叫將失敗並顯示 SecurityException

Android 9及更高版本

成功呼叫 WifiManager.startScan() 需要滿足以下所有條件:

  • 您的應用具有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 許可權。
  • 您的應用具有 CHANGE_WIFI_STATE 許可權。

成功呼叫 WifiManager.getScanResults() 需要滿足以下所有條件:

  • 您的應用具有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 許可權。
  • 您的應用具有 ACCESS_WIFI_STATE 許可權。
  • 裝置上啟用了位置服務(在“設定”>“位置”下)。

如果呼叫應用程式不滿足所有這些要求,則呼叫將失敗並顯示 SecurityException

類似的限制也適用於 getConnectionInfo() 函式,該函式返回描述當前 Wi-Fi 連線的 WifiInfo 物件。 如果呼叫應用具有以下許可權,則只能使用該物件的函式來檢索 SSIDBSSID 值:

  • ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
  • ACCESS_WIFI_STATE 檢索 SSIDBSSID 還需要在裝置上啟用位置服務(在 Settings > Location 下)。

使用證照的主機名驗證

RFC 2818 中,回退到 CN 已被棄用。因此,Android 不再回退到使用 CN。 要驗證主機名,伺服器必須出示具有匹配 SAN 的證照。 不包含與主機名匹配的 SAN 的證照不再被信任

1.4 其他限制

1.4.1 Apache HTTP 客戶端棄用影響採用非標準 ClassLoader 的應用

其實,自 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 一致。

bootclasspathlinux 系統變數,是系統在啟動時會預先載入的類,以提高系統效能,這是 小米 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:/ $ 
複製程式碼

是沒有 apachehttp 庫的, 但是 他們有一個共同特點,就是系統內建了 apache 包, 在 /system/framework/目錄下:

Android P 適配指南

,但是我有一個困惑的地方,就是 同樣 是執行在 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?這個有待研究。

1.5 電源管理優化

Android 9 引入了一項新的電池管理功能,即應用待機群組。 應用待機群組可以基於應用最近使用時間和使用頻率,幫助系統排定應用請求資源的優先順序。 根據使用模式,每個應用都會歸類到 五個 優先順序群組之一中。 系統將根據應用所屬的群組限制每個應用可以訪問的裝置資源:

活躍

如果使用者當前正在使用應用,應用將被歸到“活躍”群組中,例如:

  • 應用已啟動一個 Activity
  • 應用正在執行前臺服務
  • 應用的同步介面卡與某個前臺應用使用的 content provider 關聯
  • 使用者在應用中點選了某個通知 如果應用處於“活躍”群組,系統不會對應用的作業、報警或 FCM 訊息施加任何限制。

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
複製程式碼

2. 針對Taget 28+ 的應用

2.1 前臺服務

前臺服務 可以讓你應用處於活躍狀態,上面提到過 前臺服務 可以讓你的應用分組處於 活躍分組

Target 28+ 並使用前臺服務的應用必須請求 FOREGROUND_SERVICE 許可權。 這是 普通許可權,因此,系統會自動為請求許可權的應用授予此許可權。

2.2 隱私權變更

構建序列號棄用

在 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");

複製程式碼

DNS 隱私

以 Android 9 為目標平臺的應用應採用私有 DNS API。 具體而言,當系統解析程式正在執行 DNS-over-TLS 時,應用應確保任何內建 DNS 客戶端均使用加密的 DNS 查詢與系統相同的主機名,或停用它而改用系統解析程式。 停用路徑: Settings -> private dns:

Android P 適配指南
private DNS 設定

2.3 安全

預設情況下啟用網路傳輸層安全協議 (TLS)

如果您的應用 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 數

3. 新功能

Kotlin 友好

改進了 dexer 以及協調Jetbranins改進 Kotlin Compier 使用 kotlin 編寫的App在android P 上執行得更快,並且和 Jetbrains 團隊溝通,提供了新的 kotlin 外掛。

ImageDecoder

替代 BitmapFactory 可以從 流、file、byte buffer、 uRL 載入 BitmapDrawable 支援精確尺寸縮放, 並且支援載入 gif 、 Webp, 以及圓角等樣式設定。

wifi RTT

在室內,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" />
複製程式碼

PrecomputedText

顯示文字可能很複雜,包含多種字型,行間距,字母間距,文字方向,換行符,連字元等功能。TextView必須做很多工作來測量和佈置給定的文字:讀取字型檔案,查詢字形,確定形狀,測量邊界框以及在內部字快取中快取單詞。更重要的是,所有這些工作都發生在 UI執行緒 上,它可能會導致您的應用程式 丟幀 測量文字可能佔用設定文字所需時間的 90%

android P 正式引入, 對於 android P 之前通過 JetpackPrecomputedTextCompat使用.

Android P 適配指南
PrecomputedText

// 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
    }
}
複製程式碼

Magnifier

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)沒有相關相容類使用。

DEX 檔案的 ART 提前轉換

在執行 Android 9 或更高版本的裝置上,Android 執行時 (ART) 提前編譯器通過將應用軟體包中的 DEX 檔案轉換為更緊湊的表示形式,進一步優化了壓縮的 Dalvik Executable 格式 (DEX) 檔案。 此項變更可讓您的應用啟動更快並消耗更少的磁碟空間和記憶體。

這種改進特別有利於磁碟 I/O 速度較慢的低端裝置。

參考資料

相關文章