選擇適當的程式庫

如要啟用應用程式最佳化功能,您必須使用與 Android 最佳化相容的程式庫。如果程式庫未針對 Android 最佳化進行設定 (例如,如果使用反射而未綁定相關的保留規則),可能就不適合用於 Android 應用程式。本頁說明為何某些程式庫更適合用於應用程式最佳化,並提供一般提示協助您做出選擇。

優先使用 codegen 而非反射

一般來說,您應選擇使用程式碼產生 (codegen) 而非反射的程式庫。有了 codegen,最佳化工具就能更輕鬆地判斷在執行階段實際使用的程式碼,以及可移除的程式碼。很難判斷程式庫是否使用 codegen 或反射,但還是有一些徵兆,請參閱提示瞭解如何判斷。

如要進一步瞭解 codegen 與反射的差異,請參閱「程式庫作者的最佳化」。

選擇程式庫時的一般訣竅

請參考下列提示,確保您的程式庫與應用程式最佳化相容。

檢查最佳化問題

考慮採用新程式庫時,請查看程式庫的問題追蹤器和線上討論,確認是否有與縮減或設定應用程式最佳化相關的問題。如果有,請嘗試尋找該程式庫的替代方案。請注意以下事項:

  • AndroidX 程式庫Hilt 等程式庫可有效提升應用程式效能,因為這些程式庫使用 codegen 而非反射。使用反射時,會提供最少的保留規則,只保留所需的程式碼。
  • 序列化程式庫經常使用反射,以便在例項化或序列化物件時避免使用樣板程式碼。請尋找使用 codegen 的程式庫,以避免這些問題,例如使用 Kotlin 序列化
  • 請盡量避免使用含有套件級別保留規則的程式庫。套件層級的保留規則有助於解決錯誤,但最終應精簡廣泛的保留規則,只保留所需的程式碼。詳情請參閱「逐步採用最佳化功能」。

在新增資料庫後啟用最佳化功能

新增程式庫後,請啟用最佳化功能,並檢查是否有錯誤。如果發生錯誤,請尋找該程式庫的替代方案,或撰寫保留規則。如果程式庫與最佳化不相容,請回報該程式庫的錯誤。

規則屬於外加

請注意,保留規則是外加規則,也就是說,程式庫依附元件中包含的特定規則無法移除,且可能會影響應用程式其他部分的編譯。舉例來說,如果程式庫包含停用程式碼最佳化的規則,該規則會停用整個專案的最佳化功能。

檢查是否使用反射 (進階)

您或許可以透過檢查程式庫的程式碼,判斷該程式庫是否使用反射功能。如果程式庫使用反射,請確認它提供相關的保留規則。如果程式庫執行下列操作,可能會使用反射功能:

  • 使用 kotlin.reflectjava.lang.reflect 套件的類別或方法
  • 使用 Class.forNameclassLoader.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 分析這段程式碼,但未在任何地方看到 UserListUser 例項化時,它可能會重新命名欄位,或移除似乎未使用的建構函式,導致應用程式當機。如果您以類似方式使用任何其他程式庫,請確認這些程式庫不會干擾應用程式最佳化作業,如果會干擾,請避免使用。

請注意,RoomHilt 都會建構應用程式定義的類型,但會使用 codegen 避免需要使用反射。