身為程式庫作者,您必須確保應用程式開發人員能輕鬆將程式庫併入應用程式,同時維持高品質的使用者體驗。也就是說,程式庫必須與 Android 最佳化功能 (R8) 相容,開發人員不需額外設定,或註明程式庫可能不適合在 Android 上使用。請務必注意,用於 Android 的程式庫不得妨礙重要的應用程式最佳化作業,且必須遵守其他最佳化規定。
本文件適用於已發布程式庫的開發人員,但對於大型模組化應用程式中的內部程式庫模組開發人員,也可能有所幫助。
如果您是應用程式開發人員,想瞭解如何最佳化 Android 應用程式,請參閱「啟用應用程式最佳化功能」。如要瞭解適合使用的程式庫,請參閱「謹慎選擇程式庫」。
瞭解保留規則類型
程式庫的保留規則分為兩種:
- 消費者保留規則必須指定規則,保留程式庫反射的任何內容。如果程式庫使用反射或 JNI 呼叫程式碼,或是用戶端應用程式定義的程式碼,這些規則必須說明需要保留的程式碼。程式庫應封裝消費者保留規則,這些規則與應用程式保留規則的格式相同。這些規則會與程式庫構件 (AAR 或 JAR) 綁在一起,並在您使用程式庫時,於 Android 應用程式最佳化期間自動套用。這些規則會保留在
build.gradle.kts(或build.gradle) 檔案中,以consumerProguardFiles屬性指定的檔案中。詳情請參閱「撰寫消費者保留規則」。 - 建構程式庫時,系統會套用程式庫建構保留規則。只有在建構期間決定部分最佳化程式庫時,才需要使用這些檔案。他們必須避免移除程式庫的公開 API,否則公開 API 不會出現在程式庫發行版本中,這表示應用程式開發人員無法使用該程式庫。這些規則會保留在
build.gradle.kts(或build.gradle) 檔案中,以proguardFiles屬性指定的檔案中。詳情請參閱「最佳化 AAR 程式庫建構作業」。
最佳化規定和指南
程式庫中的 R8 設定會對消費者應用程式的最終二進位檔大小和效能產生全域影響。除了保留規則的一般最佳做法外,程式庫作者也必須遵守特定規定,並考量其他指南。
遵循最佳化規定
程式庫效率不彰是導致應用程式膨脹、記憶體浪費、啟動緩慢和 ANR (應用程式無回應錯誤) 的主要原因。程式庫必須避免違反下列規定,以免大幅降低應用程式品質和使用者體驗。
沒有廣泛或套件範圍的保留規則:程式庫不得包含廣泛的保留規則,保留程式庫或另一個程式庫中的大部分程式碼。廣泛保留規則或許能短期解決當機問題,但會增加所有使用程式庫的應用程式大小。
請勿在程式庫或其他參照程式庫中,加入套件層級的保留規則 (例如
-keep class com.mylibrary.** {*; })。這類規則會限制所有使用該程式庫的應用程式對這些套件進行最佳化。請勿針對程式庫使用的第三方依附元件 (例如
-keep public class com.google.android.gms.** { *; }) 加入廣泛的保留規則。許多外部程式庫已提供自己的消費者保留規則;引入套件範圍規則會取代這些規則,並強制 R8 保留整個應用程式中未參照的依附元件程式碼。不使用不當的全域規則:請勿使用
-dontobfuscate或-allowaccessmodification等全域選項。盡可能使用程式碼生成,而非反射:請盡可能使用程式碼生成 (codegen),而非反射。程式碼生成和反射都是避免設計程式時使用樣板程式碼的常見方法,但程式碼生成與 R8 等應用程式最佳化工具的相容性較高。
使用程式碼產生功能時,系統會在建構程序中分析及修改程式碼。由於編譯時間後不會有任何重大修改,最佳化工具會知道最終需要哪些程式碼,以及哪些程式碼可以安全移除。
使用反射時,程式碼會在執行階段進行分析和操控。由於程式碼在執行前並未真正完成,因此最佳化工具不知道可以安全移除哪些程式碼。這可能會移除在執行階段透過反射動態使用的程式碼,導致應用程式當機。
許多現代程式庫都使用程式碼生成,而非反射。如需常見的進入點,請參閱 KSP,Room、Hilt 和許多其他項目都會使用這個進入點。
支援 R8 完整模式:啟用 R8 完整模式時,程式庫不應當機。R8 的完整模式是建議使用的模式,自 2023 年穩定版 AGP 8.0 起,完整模式即為預設模式。如果程式庫在 R8 下當機,解決方法是找出特定的反射或 JNI 進入點,並新增指定規則,而不是保留整個套件。如要根據這些進入點的註解產生指定規則,請使用 Jetpack Keep。
其他建議
除了最佳化規定外,以下是其他建議。
請勿在程式庫的消費者保留規則檔案中使用
-repackageclasses。消費者保留規則會合併到每個使用程式庫的應用程式 R8 設定中,因此這個全域選項會套用至整個應用程式,並覆寫應用程式開發人員自己的重新封裝設定。根據預設,自 AGP 9.0 起,如果程式庫的消費者保留規則包含全域選項,發布程式庫就會失敗。詳情請參閱 AGP 9.0 版本資訊。如要最佳化程式庫建構作業,您可以在程式庫的建構保留規則檔案中,改用
-repackageclasses搭配內部套件名稱,例如<your.library.package>.internal。這可以提升程式庫在未最佳化應用程式中的效率,但通常不需要這麼做,因為應用程式也應經過最佳化。在程式庫的保留規則檔案中,宣告程式庫運作所需的任何屬性,即使這些屬性可能與
proguard-android-optimize.txt中定義的屬性重疊也一樣。如果您在程式庫發行版中需要下列屬性,請將這些屬性保留在程式庫的建構保留規則檔案中,而「不要」保留在程式庫的使用者保留規則檔案中:
AnnotationDefaultEnclosingMethodExceptionsInnerClassesRuntimeInvisibleAnnotationsRuntimeInvisibleParameterAnnotationsRuntimeInvisibleTypeAnnotationsRuntimeVisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeVisibleTypeAnnotationsSignature
如果在執行階段使用註解,程式庫作者應在消費者保留規則中保留
RuntimeVisibleAnnotations屬性。程式庫作者不應在消費者保留規則中使用下列全域選項。與
-repackageclasses相同,這些選項會設定應用程式的整個 R8 建構作業,而非僅設定程式庫的程式碼,因此請將這些選項留給應用程式開發人員:-include-basedirectory-injars-outjars-libraryjars-repackageclasses-flattenpackagehierarchy-allowaccessmodification-renamesourcefileattribute-ignorewarnings-addconfigurationdebugging-printconfiguration-printmapping-printusage-printseeds-applymapping-obfuscationdictionary-classobfuscationdictionary-packageobfuscationdictionary
適合使用反射的情況
如果必須使用反射,則只能反射到下列其中一項:
- 特定指定類型 (特定介面實作者或子類別)
- 使用特定執行階段註解的程式碼
以這種方式使用反射,可限制執行階段成本,並編寫指定消費者保留規則。
這種特定且有針對性的反射形式是一種模式,您可以在 Android 架構 (例如,在加載活動、檢視畫面和可繪項目時) 和 AndroidX 程式庫 (例如,在建構 WorkManager
ListenableWorkers 或 RoomDatabases 時) 中看到。相較之下,Gson 的開放式反射不適合在 Android 應用程式中使用。
常見的誤解
一些常見的錯誤觀念可能會導致 R8 設定錯誤。其中包括:
對 R8 最佳化功能的誤解:與一般認知相反,R8 的最佳化功能不限於模糊處理,還包括程式碼縮減,以及使用方法內嵌和類別合併技術進行邏輯最佳化。詳情請參閱「R8 最佳化總覽」。
略過經過模糊處理的程式庫最佳化:常見的錯誤是略過程式庫最佳化,因為程式庫在編譯為 AAR (Android 封存檔) 或 JAR (Java 封存檔) 時,已完成最佳化或模糊處理。程式庫建構期間的最佳化作業有限,應用程式不應將程式庫納入保留規則,以免停用程式庫的最佳化功能。詳情請參閱「最佳化 AAR 程式庫建構作業」。
驗證消費者保留規則品質
如要驗證程式庫嵌入應用程式時,不會過度阻礙程式碼最佳化,請使用程式庫範例或整合測試應用程式。啟用 R8,並使用 R8 設定分析器檢查應用程式的整體 R8 設定品質。
如果消費者規則限制了程式庫中大部分程式碼的最佳化,表示消費者規則需要調整。縮小消費者規則範圍,並在已啟用 R8 的應用程式中,測試程式庫任何受影響的部分。
設定規則封裝
為確保消費者保留規則正確套用,您必須根據程式庫格式適當封裝。
如果您使用 Jetpack Keep 宣告程式庫的反射和 JNI 用途,其 Gradle 外掛程式可以將產生的規則封裝為消費者保留規則。
AAR 程式庫
如要為 AAR 程式庫新增消費者規則,請在 Android 程式庫模組的建構指令碼中使用 consumerProguardFiles 選項。詳情請參閱建立程式庫模組的指南。
Kotlin
android {
defaultConfig {
consumerProguardFiles("consumer-proguard-rules.pro")
}
...
}
Groovy
android {
defaultConfig {
consumerProguardFiles 'consumer-proguard-rules.pro'
}
...
}
JAR 程式庫
如要將規則與以 JAR 形式發布的 Kotlin 或 Java 程式庫組合在一起,請將規則檔案放在最終 JAR 的 META-INF/proguard/ 目錄中,檔名可自行命名。舉例來說,如果您的程式碼位於 <libraryroot>/src/main/kotlin,請將消費者規則檔案放在 <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro,規則就會在輸出 JAR 中正確的位置進行組合。
檢查規則是否位於 META-INF/proguard 目錄中,確認最終 JAR 是否正確繫結規則。
最佳化 AAR 程式庫建構作業 (進階)
一般來說,您不需要直接最佳化程式庫建構作業,因為在程式庫建構期間,可進行的最佳化作業非常有限。身為程式庫開發人員,您需要考量多個最佳化階段,並在最佳化程式庫之前,保留程式庫和應用程式建構時的行為。
如果您仍想在建構時最佳化程式庫,Android Gradle 外掛程式支援這項功能。
Kotlin
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
configureEach {
consumerProguardFiles("consumer-rules.pro")
}
}
}
Groovy
android {
buildTypes {
release {
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
configureEach {
consumerProguardFiles "consumer-rules.pro"
}
}
}
請注意,proguardFiles 的行為與 consumerProguardFiles 有很大的不同:
proguardFiles在建構期間使用,通常會與getDefaultProguardFile("proguard-android-optimize.txt")搭配,定義程式庫建構時應保留的程式庫部分。至少要提供公開 API。consumerProguardFiles則會封裝到程式庫中,用來決定使用該程式庫的應用程式建置時需要執行哪些優化。
舉例來說,如果程式庫使用反射來建構內部類別,您可能需要在 proguardFiles 和 consumerProguardFiles 中定義保留規則。
如果您在程式庫建構作業中使用 -repackageclasses,請將類別重新封裝至程式庫套件「內」的子套件。例如,使用 -repackageclasses
'com.example.mylibrary.internal',而不使用 -repackageclasses 'internal'。
支援不同的 R8 版本 (進階)
您可以自訂規則,指定特定版本的 R8。這樣一來,程式庫就能在使用較新 R8 版本的專案中發揮最佳效能,同時允許現有規則繼續在使用較舊 R8 版本的專案中使用。
如要指定 R8 規則,您必須將規則加入 AAR 的 classes.jar 內的 META-INF/com.android.tools 目錄,或 JAR 的 META-INF/com.android.tools 目錄。
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
在 META-INF/com.android.tools 目錄中,可以有多個子目錄,其名稱格式為 r8-from-<X>-upto-<Y>,表示規則是針對哪些 R8 版本編寫。每個子目錄可以有一或多個包含 R8 規則的檔案,檔案名稱和副檔名不限。
請注意,-from-<X> 和 -upto-<Y> 部分為選用,<Y> 版本為「不含」,版本範圍通常是連續的,但也可能重疊。
舉例來說,r8、r8-upto-8.0.0、r8-from-8.0.0-upto-8.2.0 和 r8-from-8.2.0 是代表一組指定 R8 規則的目錄名稱。r8 目錄下的規則可供任何 R8 版本使用。r8-from-8.0.0-upto-8.2.0 目錄下的規則可由 R8 使用,版本範圍為 8.0.0 至 8.2.0 (不含)。
Android Gradle 外掛程式會根據這項資訊,選取目前 R8 版本可用的所有規則。如果程式庫未指定目標 R8 規則,Android Gradle 外掛程式會從舊版位置選取規則 (AAR 的 proguard.txt 或 JAR 的 META-INF/proguard/<ProGuard-rule-files>)。