遵循最佳做法

使用保留規則時,請務必達到適當的精確度,確保您能獲得好處,同時維持應用程式的行為。如要瞭解良好的保留規則模式,以及應避免的事項,請參閱以下各節。

保留規則的良好模式

明確定義的保留規則會盡可能具體,並遵循下列模式:

  • 如為類別規格,請盡可能指定特定類別、基本類別或註解類別,如下列範例所示:

    -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 不再保留這些項目。

如果無法遵守這些規範,可以暫時將需要保留的程式碼隔離到專屬套件中,並將保留規則套用至該套件。但這並非長久之計。詳情請參閱「逐步採用最佳化措施」。如要為套件使用保留規則,請定義保留規則,如下列範例所示:

-keepclassmembers class com.example.pkg.** { *; }

應避免的事項

保留規則語法有很多選項,但為了獲得可衡量的永續效能優勢,建議不要使用下列項目:

  • 請勿長期使用套件範圍的保留規則,例如 -keep class com.example.pkg.** { *; }。設定 R8 時,可暫時使用這些規則解決問題。詳情請參閱「限制最佳化範圍」。一般來說,請謹慎使用萬用字元,確保只保留所需的程式碼。
  • 請盡可能避免使用建議您複製及貼上保留規則的程式庫,尤其是套件範圍的保留規則。為確保程式庫在 Android 上順利運作,請盡可能避免使用反射,並在必要時嵌入消費者保留規則
  • 請避免在保留規則中使用反轉運算子 !,因為您可能會不小心將規則套用至應用程式中的幾乎每個類別。

如果無法遵守這些規則,您可能使用了大量開放式反射,因此應避免反射或避免使用反射的程式庫 (請參閱 Gson 個案研究)。