使用保留規則時,請務必達到適當的精細程度,確保您能獲得好處,同時維持應用程式的行為。請參閱下列各節,瞭解保留規則的良好模式和應避免的事項。
保留規則的良好模式
明確定義的保留規則應盡可能具體,並遵守下列模式:
在類別規格中,請盡可能指定特定類別、基礎類別或加上註解的類別,如以下範例所示:
-keepclassmembers class com.example.MyClass { void someSpecificMethod(); }-keepclassmembers ** extends com.example.MyBaseClass { void someSpecificMethod(); }-keepclassmembers @com.example.MyAnnotation class ** { void someSpecificMethod(); }請盡可能在原始碼中使用註解,然後在保留規則中直接指定這些註解。這會在程式碼和保留程式碼的規則之間建立明確的連結,讓設定更穩固、更容易瞭解,且程式碼變更時較不容易中斷。
舉例來說,下列程式碼片段示範如何保留 MyClass 類別,以及其他加上
@com.example.DisplayComponent註解的類別:// In the source code @com.example.DisplayComponent class MyClass { /* ... */ } // In the keep rules -keep @com.example.DisplayComponent class * {*;}建議您為註解命名,提供有意義的背景資訊,說明保留程式碼部分的原因。舉例來說,如果應用程式的顯示元件需要保留部分內容,請使用
@DisplayComponent。請盡可能宣告成員規格,並僅參照應用程式運作時必須保留的類別部分。建議不要將規則套用至整個類別,方法是將選用成員範圍定義為
{ *; },除非有嚴格需求。-keepclassmembers com.example.MyClass { void someSpecificMethod(); void @com.example.MyAnnotation *; }如果您使用
repackageclasses全域選項,請避免指定選用套件名稱。由於重新封裝的類別名稱省略了套件前置字元,因此 DEX 檔案會變小。為所有透過反射存取的項目保留規則。即使 R8 在沒有明確保留規則的情況下保留這些項目,維護實際規則仍至關重要,因為日後程式碼變更可能會導致 R8 不再保留這些項目。
如要在自己的程式碼中使用反射,可以使用 Jetpack Keep 產生更容易維護的保留規則。每個註解都位於執行反射的程式碼上,因此當您變更該程式碼時,您會在同一位置更新或移除其註解。這樣一來,規則就不會保留應用程式不再反映的程式碼。Lint 也會標記未加上註解的反思呼叫。
盡可能使用
-assumenosideeffects或-convertchecknotnull最佳化空值檢查,因為這樣可以大幅縮減應用程式的 DEX 大小。詳情請參閱「其他規則類型」。
如果無法遵守這些規範,可以暫時將需要保留的程式碼隔離在專屬套件中,並對該套件套用保留規則。不過,這並非長久之計。詳情請參閱「逐步採用最佳化功能」。如要對套件使用保留規則,請定義保留規則,如以下範例所示:
-keepclassmembers class com.example.pkg.** { *; }
應避免的事項
保留規則語法有很多選項,但為了獲得可衡量的永續效能優勢,建議不要使用下列選項:
- 請勿長期使用套件層級的保留規則,例如
-keep class com.example.pkg.** { *; }。在設定 R8 時,可以暫時使用這些選項來解決問題。詳情請參閱「限制最佳化範圍」。一般來說,請謹慎使用萬用字元,確保只保留需要的程式碼。 - 請盡可能避免使用建議您在使用時複製及貼上保留規則的程式庫,尤其是套件層級的保留規則。為確保程式庫在 Android 上順利運作,請盡可能避免使用反射,並在必要時嵌入消費者保留規則。
- 請避免在保留規則中使用反轉運算子
!,因為您可能會不小心將規則套用至應用程式中的幾乎所有類別。
如果您無法遵循這些規則,可能表示您使用大量開放式反射,因此應避免反射,或避免使用反射的程式庫 (請參閱 Gson 個案研究)。