如要啟用應用程式最佳化功能,您必須使用與 Android 最佳化相容的程式庫。如果程式庫未針對 Android 最佳化進行設定 (例如,如果使用反射而未綁定相關的保留規則),可能就不適合用於 Android 應用程式。本頁說明為何某些程式庫更適合用於應用程式最佳化,並提供一般提示協助您做出選擇。
優先使用 codegen 而非反射
一般來說,您應選擇使用程式碼產生 (codegen) 而非反射的程式庫。有了 codegen,最佳化工具就能更輕鬆地判斷在執行階段實際使用的程式碼,以及可移除的程式碼。很難判斷程式庫是否使用 codegen 或反射,但還是有一些徵兆,請參閱提示瞭解如何判斷。
如要進一步瞭解 codegen 與反射的差異,請參閱「程式庫作者的最佳化」。
選擇程式庫時的一般訣竅
請參考下列提示,確保您的程式庫與應用程式最佳化相容。
檢查最佳化問題
考慮採用新程式庫時,請查看程式庫的問題追蹤器和線上討論,確認是否有與縮減或設定應用程式最佳化相關的問題。如果有,請嘗試尋找該程式庫的替代方案。請注意以下事項:
- AndroidX 程式庫和 Hilt 等程式庫可有效提升應用程式效能,因為這些程式庫使用 codegen 而非反射。使用反射時,會提供最少的保留規則,只保留所需的程式碼。
- 序列化程式庫經常使用反射,以便在例項化或序列化物件時避免使用樣板程式碼。請尋找使用 codegen 的程式庫,以避免這些問題,例如使用 Kotlin 序列化。
- 請盡量避免使用含有套件級別保留規則的程式庫。套件層級的保留規則有助於解決錯誤,但最終應精簡廣泛的保留規則,只保留所需的程式碼。詳情請參閱「逐步採用最佳化功能」。
在新增資料庫後啟用最佳化功能
新增程式庫後,請啟用最佳化功能,並檢查是否有錯誤。如果發生錯誤,請尋找該程式庫的替代方案,或撰寫保留規則。如果程式庫與最佳化不相容,請回報該程式庫的錯誤。
規則屬於外加
請注意,保留規則是外加規則,也就是說,程式庫依附元件中包含的特定規則無法移除,且可能會影響應用程式其他部分的編譯。舉例來說,如果程式庫包含停用程式碼最佳化的規則,該規則會停用整個專案的最佳化功能。
檢查是否使用反射 (進階)
您或許可以透過檢查程式庫的程式碼,判斷該程式庫是否使用反射功能。如果程式庫使用反射,請確認它提供相關的保留規則。如果程式庫執行下列操作,可能會使用反射功能:
- 使用
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 都會建構應用程式定義的類型,但會使用 codegen 避免需要使用反射。