選擇適當的程式庫

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

選擇程式庫的一般提示

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

使用程式碼生成而非反射

選擇使用程式碼生成 (codegen) 而非反射的程式庫。有了程式碼產生功能,最佳化工具就能判斷哪些程式碼在執行階段實際使用,哪些程式碼可以移除。雖然很難判斷程式庫使用的是程式碼生成還是反射,但還是有一些跡象可循,請參閱提示。

如要進一步瞭解程式碼產生與反射,請參閱「程式庫作者最佳化」。

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

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

  • 使用 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)
    

檢查最佳化問題

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

  • AndroidX 程式庫和 Hilt 等程式庫與應用程式最佳化功能搭配良好,因為這些程式庫大多使用程式碼生成,而非反射。如果使用反射,則會提供最少的保留規則,只保留必要的程式碼。
  • 序列化程式庫經常使用反射,避免在例項化或序列化物件時產生樣板程式碼。請尋找使用程式碼生成技術的程式庫,避免採用反射式方法 (例如 JSON 的 Gson),例如使用 Kotlin 序列化或搭配程式碼生成的 Moshi。
  • 請盡可能避免使用包含套件層級保留規則的程式庫。套件層級的保留規則有助於解決錯誤,但廣泛的保留規則最終應經過精簡,只保留必要的程式碼。詳情請參閱「逐步採用最佳化功能」。
  • 發布使用第三方程式庫的應用程式前,請先透過 R8 設定分析器稽核其提供的保留規則。查看報告後,您可以確認程式庫的保留規則是否過於廣泛,導致 R8 無法對程式碼集執行重要最佳化。這項檢查可確保您選取的程式庫符合應用程式的成效目標,且不會造成不必要的設定膨脹。
  • 程式庫不應要求您將 Keep 規則從說明文件複製及貼上至專案中的檔案,尤其是套件層級的 Keep 規則。長期來看,這些規則會成為應用程式開發人員的維護負擔,且難以隨著時間進行最佳化和變更。

新增程式庫後啟用最佳化功能

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

篩除不當的保留規則 (進階)

保留規則屬於外加規則。這表示程式庫依附元件包含的某些規則無法移除,且可能會影響應用程式其他部分的編譯。舉例來說,如果程式庫包含停用程式碼最佳化的規則,該規則會停用整個專案的最佳化。

請避免使用保留規則會保留應移除程式碼的程式庫。但如果必須使用,可以按照下列程式碼篩除規則:

// 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")
        }
    }
}

廣泛程式庫規則的建議工作流程

如果 R8 設定分析器顯示程式庫包含過於廣泛的保留規則,請只在本機建構作業中篩選這些規則,不要在正式版建構作業中篩選。如要測試規則對應用程式的影響程度,請按照下列步驟操作:

  1. 更新程式庫:請確認您使用的是 R8 設定分析器標記的最新版本程式庫,因為新版本可能提供更精確的消費者保留規則。如要查看最新版本,請參閱程式庫說明文件或 Maven 存放區。Android Studio 也會標記許多依附元件的新版本。更新後,請再次執行 R8 設定分析器,檢查是否仍有廣泛的保留規則。
  2. 在本機檢查潛在改善項目:將程式庫隨附的消費者規則複製到本機保留規則檔案,篩選出隨附規則 (如「篩選出不當保留規則」所示),並縮小複製規則的範圍。在縮小規則範圍前後,執行 R8 設定分析器,比較應用程式的縮減、最佳化和模糊化分數。
  3. 回報結果:向程式庫維護人員提交錯誤報告,其中包含 R8 設定分析器報告中的經驗資料 (例如,說明萬用字元規則保留的未參照類別或方法具體數量)。

個案研究: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 如何在 Gson 上運作,請參閱 Gson 消費者規則。當 R8 分析這段程式碼,且未在任何地方看到 UserList 或 User 例項化時,可能會重新命名欄位,或移除看似未使用的建構函式,導致應用程式當機。如果您以類似方式使用其他程式庫,請確認這些程式庫不會干擾應用程式最佳化,否則請避免使用。

搭配使用 @SerializedName 和較新版 Gson

如要以符合 Gson 消費者規則的方式定義資料模型,請使用 @SerializedName 註解欄位,如以下程式碼片段所示:

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

使用 @SerializedName 註解,即可讓 R8 根據 Gson 2.11.0 以上版本隨附的保留規則,比對模型類別。R8 會自動保留加上註解的欄位和必要的建構函式,讓您能依賴程式庫的套裝規則,而不必在專案中手動維護 ProGuard 設定。

請注意,Room、Hilt 和使用 codegen 的 Moshi 會建構應用程式定義的類型,但會使用 codegen 避免需要反射。