身為程式庫作者,您應確保應用程式開發人員能輕鬆將程式庫併入應用程式,同時維持高品質的終端使用者體驗。請確保程式庫與 Android 最佳化功能相容,不需要額外設定,或記錄程式庫可能不適合在 Android 上使用。
這份文件適用於已發布程式庫的開發人員,但對於大型模組化應用程式的內部程式庫模組開發人員來說,也可能很有幫助。
如果您是應用程式開發人員,想瞭解如何最佳化 Android 應用程式,請參閱「啟用應用程式最佳化功能」。如要瞭解適合使用的程式庫,請參閱「明智地選擇程式庫」。
使用程式碼產生功能而非反映功能
請盡可能使用程式碼生成 (codegen),而非反射。程式碼產生和反射都是避免程式設計時使用樣板程式碼的常見方法,但程式碼產生與 R8 等應用程式最佳化工具的相容性較高:
- codegen 會在建構程序中分析及修改程式碼。 由於編譯後不會有重大修改,最佳化工具知道最終需要哪些程式碼,以及哪些程式碼可以安全移除。
- 使用反射時,系統會在執行階段分析及操控程式碼。因為程式碼要到執行時才會真正完成,因此最佳化工具無法判斷哪些程式碼可以安全移除。這可能會移除在執行階段透過反射動態使用的程式碼,導致使用者應用程式當機。
許多新式程式庫都使用程式碼產生,而非反射。請參閱 KSP,瞭解 Room、Dagger2 和許多其他項目使用的常見進入點。
適合使用鏡像投影的情況
如果必須使用反射,則只能反射到下列任一項目:
- 特定目標類型 (特定介面實作工具或子類別)
- 使用特定執行階段註解編寫程式碼
以這種方式使用反射可限制執行階段成本,並啟用目標消費者保留規則。
這種特定且有目標的反射形式,是您在 Android 架構 (例如膨脹活動、檢視區塊和可繪項目時) 和 AndroidX 程式庫 (例如建構 WorkManager
ListenableWorkers
或 RoomDatabases
時) 中都能看到的模式。相較之下,Gson 的開放式反射不適合在 Android 應用程式中使用。
圖書館中的保留規則類型
圖書館中的保留規則有兩種:
- 消費者保留規則必須指定規則,保留程式庫反映的任何內容。如果程式庫使用反射或 JNI 呼叫程式碼,或是用戶端應用程式定義的程式碼,這些規則必須說明需要保留的程式碼。程式庫應封裝取用端保留規則,這些規則的格式與應用程式保留規則相同。這些規則會封裝到程式庫構件 (AAR 或 JAR) 中,並在您使用程式庫時,於 Android 應用程式最佳化期間自動採用。這些規則會保留在
build.gradle.kts
(或build.gradle
) 檔案中,以consumerProguardFiles
屬性指定的檔案中。詳情請參閱「撰寫消費者保留規則」。 - 程式庫建構保留規則會在建構程式庫時套用。如果您決定在建構時部分最佳化程式庫,才需要這些檔案。他們必須確保程式庫的公開 API 不會遭到移除,否則程式庫發布時不會包含公開 API,應用程式開發人員就無法使用該程式庫。這些規則會保留在
build.gradle.kts
(或build.gradle
) 檔案中,以proguardFiles
屬性指定的檔案。詳情請參閱「最佳化 AAR 程式庫建構作業」。
撰寫消費者保留規則
除了保留規則指南外,以下是專為程式庫作者提供的建議。
- 請勿使用不當的全域規則,避免在程式庫的消費者保留規則檔案中加入
-dontobfuscate
或-allowaccessmodification
等全域設定,因為這類設定會影響所有使用程式庫的應用程式。 - 請勿在程式庫的消費者保留規則檔案中使用
-repackageclasses
。不過,如要最佳化程式庫建構作業,您可以在程式庫的建構保留規則檔案中,使用-repackageclasses
和內部套件名稱 (例如<your.library.package>.internal
)。即使使用程式庫的應用程式未經過最佳化,這項功能也能提升程式庫效率,但一般來說不需要這麼做,因為應用程式也應進行最佳化。如要進一步瞭解如何最佳化程式庫,請參閱「程式庫作者的最佳化做法」。 - 即使可能與
proguard-android-optimize.txt
中定義的屬性重疊,您仍須在程式庫的保留規則檔案中,宣告程式庫運作所需的任何屬性。 - 如果您需要在程式庫發布作業中加入下列屬性,請在程式庫的建構保留規則檔案中維護這些屬性,不要在程式庫的消費者保留規則檔案中維護:
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- 如果註解是在執行階段使用,程式庫作者應在消費者保留規則中保留
RuntimeVisibleAnnotations
屬性。 - 程式庫作者不應在消費者保留規則中使用下列全域選項:
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
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 程式庫建構作業調整至最佳狀態 (進階)
一般來說,您不需要直接最佳化程式庫建構作業,因為程式庫建構期間可進行的最佳化非常有限。只有在應用程式建構期間,將程式庫納入應用程式時,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 可使用 r8-from-8.0.0-upto-8.2.0
目錄下的規則,但版本必須介於 8.0.0 到 8.2.0 之間 (不含 8.2.0)。
Android Gradle 外掛程式會使用這項資訊,選取目前 R8 版本可使用的所有規則。如果程式庫未指定目標 R8 規則,Android Gradle 外掛程式會從舊版位置 (AAR 的 proguard.txt
或 JAR 的 META-INF/proguard/<ProGuard-rule-files>
) 選取規則。