作為程式庫作者,您應確保應用程式開發人員能夠輕鬆將程式庫整合至應用程式,同時維持高品質的使用者體驗。您應確保程式庫與 Android 最佳化相容,無須額外設定,或記錄程式庫可能不適合在 Android 上使用。
本文件的目標對象為已發布的程式庫開發人員,但也可能對大型模組化應用程式中內部程式庫模組的開發人員有所助益。
如果您是應用程式開發人員,並想瞭解如何最佳化 Android 應用程式,請參閱「啟用應用程式最佳化功能」。如要瞭解適合使用的程式庫,請參閱「選擇適當的程式庫」。
使用 codegen 而非反射
盡可能使用程式碼產生 (codegen) 而非反射。程式碼產生和反射都是在程式設計時避免使用樣板程式碼的常見方法,但程式碼產生功能與 R8 等應用程式最佳化工具的相容性較高:
- 使用 codegen 時,系統會在建構程序期間分析及修改程式碼。由於在編譯時間後不會有任何重大修改,最佳化工具就會知道最終需要哪些程式碼,以及哪些程式碼可以安全移除。
- 透過反射,您可以在執行階段分析及操作程式碼。由於程式碼必須執行後才能真正完成,最佳化工具就無法得知哪些程式碼可安全移除。這可能會在執行階段透過反射移除動態使用的程式碼,導致使用者遇到應用程式當機問題。
許多新型程式庫都使用 codegen 而非反射。如需常見的進入點,請參閱 KSP,這項功能會用於 Room、Dagger2 和其他許多項目。
反光效果是否可接受
如果您必須使用反射功能,請只反射下列任一項目:
- 特定指定類型 (特定介面實作者或子類別)
- 使用特定執行階段註解的程式碼
以這種方式使用反射功能可限制執行階段成本,並可編寫指定消費者保留規則。
這種特定且有目標的反射形式是您在 Android 架構 (例如在加載活動、檢視畫面和可繪項目時) 和 AndroidX 程式庫 (例如在建構 WorkManager ListenableWorker 或 RoomDatabase) 中都會看到的模式。相反地,Gson 的開放式反射功能不適合用於 Android 應用程式。
編寫用戶保留規則
程式庫應封裝「消費者」保留規則,其格式與應用程式保留規則相同。這些規則會整合至程式庫構件 (AAR 或 JAR),並在使用程式庫時,於 Android 應用程式最佳化期間自動使用。
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 的正確位置中捆綁。
確認最終 JAR 套件規則正確無誤,方法是檢查規則是否位於 META-INF/proguard
目錄中。
最佳化 AAR 程式庫建構作業 (進階)
一般來說,您不應直接最佳化程式庫建構作業,因為在程式庫建構期間,可進行的最佳化作業非常有限。只有在應用程式建構期間,當程式庫納入應用程式時,R8 才能瞭解如何使用程式庫的所有方法,以及傳遞哪些參數。作為程式庫開發人員,您需要在程式庫和應用程式建構時,考量多個最佳化階段和保留行為,然後再對該程式庫進行最佳化。
如果您仍想在建構期間最佳化程式庫,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>
) 選取規則。