如要啟用應用程式最佳化功能,您必須使用與 Android 最佳化功能相容的程式庫。如果程式庫未針對 Android 最佳化進行設定 (例如使用反射,但未一併封裝相關的保留規則),可能就不適合用於 Android 應用程式。本頁說明為何部分程式庫更適合用於應用程式最佳化,並提供一般提示,協助您做出選擇。
偏好使用程式碼產生而非反射
一般來說,您應該選擇使用程式碼產生 (codegen) 而非反射的程式庫。透過程式碼產生,最佳化工具可以更輕鬆地判斷執行階段實際使用的程式碼,以及可移除的程式碼。很難判斷程式庫是否使用程式碼產生或反射,但有一些跡象可供參考,請參閱提示。
如要進一步瞭解程式碼產生與反射,請參閱「程式庫作者的最佳化做法」。
選擇程式庫的一般訣竅
請參考這些提示,確保程式庫與應用程式最佳化功能相容。
檢查最佳化問題
考慮使用新程式庫時,請查看程式庫的問題追蹤器和線上討論,確認是否有與縮減或設定應用程式最佳化相關的問題。如果有的話,請嘗試尋找該程式庫的替代方案。請注意以下幾點:
- AndroidX 程式庫和 Hilt 等程式庫使用程式碼產生,而非反射,因此非常適合用於應用程式最佳化。如果使用反射,他們會提供最少的保留規則,只保留所需的程式碼。
- 序列化程式庫通常會使用反射,在例項化或序列化物件時避免樣板程式碼。請改用程式碼產生方法,避免發生這些問題,例如使用 Kotlin 序列化,而非以反射為基礎的方法 (例如 JSON 的 Gson)。
- 如果可以,請盡量避免使用包含套件範圍保留規則的程式庫。套件範圍的保留規則有助於解決錯誤,但最終應將廣泛保留規則調整為只保留必要的程式碼。詳情請參閱逐步採用最佳化措施。
- 程式庫不應要求您從說明文件複製並貼上 Keep 規則到專案的檔案中,尤其是套件範圍的 Keep 規則。長期來看,這些規則會成為應用程式開發人員的維護負擔,而且難以隨著時間進行最佳化和變更。
新增媒體庫後啟用最佳化功能
新增程式庫後,請啟用最佳化功能,並檢查是否有錯誤。如有錯誤,請尋找該程式庫的替代方案,或編寫保留規則。如果程式庫與最佳化功能不相容,請為該程式庫回報錯誤。
規則屬於外加規則
請注意,保留規則是外加的。也就是說,程式庫依附元件中的特定規則無法移除,可能會影響應用程式其他部分的編譯作業。舉例來說,如果程式庫包含停用程式碼最佳化的規則,該規則就會停用整個專案的最佳化作業。
檢查是否使用反射功能 (進階)
檢查程式庫的程式碼,或許就能判斷程式庫是否使用反射功能。如果程式庫使用反射,請確認程式庫提供相關的保留規則。如果程式庫執行下列操作,可能就是使用反射功能:
- 使用
kotlin.reflect或java.lang.reflect套件中的類別或方法 - 使用
Class.forName或classLoader.getClass函式 - 在執行階段讀取註解,例如使用
val value = myClass.getAnnotation()或val value = myMethod.getAnnotation()儲存註解值,然後對value執行某些操作 使用方法名稱 (字串) 呼叫方法,例如:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
篩除不當的保留規則 (進階)
您應避免使用含有保留規則的程式庫,因為這類規則會保留應移除的程式碼。但如果必須使用這些規則,可以按照下列程式碼篩除:
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
個案研究:為什麼 Gson 會因最佳化而中斷
Gson 是序列化程式庫,由於大量使用反射,因此經常導致應用程式最佳化問題。下列程式碼片段顯示 Gson 的一般用法,這很容易在執行階段造成當機。請注意,使用 Gson 取得 User 物件清單時,您不會呼叫建構函式,也不會將工廠傳遞至 fromJson() 函式。如果建構或取用應用程式定義的類別時,缺少下列任一項目,表示程式庫可能使用開放式反射:
- 實作程式庫或標準介面/類別的應用程式類別
- 程式碼生成外掛程式,例如 KSP
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
當 R8 分析這段程式碼時,如果沒有看到任何地方例項化的 UserList 或 User,就會重新命名欄位,或移除看似未使用的建構函式,導致應用程式當機。如果您以類似方式使用任何其他程式庫,請檢查這些程式庫是否會干擾應用程式最佳化,如果會,請避免使用。
請注意,Room 和 Hilt 都會建構應用程式定義的型別,但會使用程式碼產生功能,避免需要反射。