新增保留規則

使用預設設定啟用應用程式最佳化功能時,R8 會執行全面最佳化,以便充分發揮效能優勢。R8 會對程式碼進行重大修改,包括重新命名、移動及移除類別欄位和方法。如果這會導致錯誤,您必須編寫保留規則,指定要保留的程式碼部分。

在下列情況下,R8 可能會錯誤移除或修改程式碼:

  • 反映:使用反映功能存取的程式碼,例如使用 Class.forName()Method.invoke()。R8 通常無法判斷哪些類別或方法會以這種方式存取。
  • 序列化:序列化和反序列化所需的類別或欄位,可能會顯示為 R8 未使用的項目 (這是另一種反射形式)。
  • Java 原生介面 (JNI):從原生程式碼呼叫的 Java 方法。R8 不會分析原生程式碼,查看可能回呼至 Java 的內容。

本頁面將說明如何限制 R8 的最佳化程度。如要瞭解如何自訂要保留的資源,請參閱「為資源新增保留規則」。

新增保留規則的位置

您應將規則新增至位於模組根目錄中的 proguard-rules.pro 檔案 (檔案可能已在該目錄中,但如果沒有,請建立檔案)。如要套用檔案中的規則,您必須在模組層級的 build.gradle.kts (或 build.gradle) 檔案中宣告檔案,如以下程式碼所示:

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Default file with default optimization rules.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // File with your custom rules.
                "proguard-rules.pro"
            )
            ...
        }
    }
    ...
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true

            proguardFiles(
                // Default file with default optimization rules.
                getDefaultProguardFile('proguard-android-optimize.txt'),

                // File with your custom rules.
                'proguard-rules.pro'
            )
        }
    }
    // ...
}

根據預設,您的建構指令碼也會包含 proguard-android-optimize.txt 檔案。這個檔案包含大多數 Android 專案所需的規則,因此請將其保留在建構指令碼中。

如何編寫 Keep 規則

在應用程式編譯期間,R8 會分析應用程式的呼叫圖表,藉此偵測應用程式中需要保留哪些程式碼,這些圖表會從資訊清單項目 (例如活動或服務) 開始,並追蹤每個應用程式和程式庫函式呼叫。R8 會移除未以這種方式直接參照的程式碼,如果執行的程式碼並非此圖表的一部分,可能會導致問題,例如透過反射方式叫用的程式碼。您可以編寫自己的保留規則,向 R8 告知需要保留在應用程式中的程式碼。

如要新增保留規則,請在 proguard-rules.pro 檔案中加入 -keep 程式碼行。

保留規則語法

Keep 規則通常會遵循以下格式:

-<KeepOption> [OptionalModifier,...] <ClassSpecification> [{ OptionalMemberSpecification }]

舉例來說,如要保留特定類別及其所有成員,請使用以下方式:

-keep class com.myapp.MyClass { *; }

如需更多範例,請參閱範例一節。

以下說明保留規則元件的運作方式:

  • <KeepOption> 可讓您指定要保留的類別層面:

    Keep 選項 說明

    -keep

    保留 [{ OptionalMemberSpecification }] 中列出的類別和成員。

    -keepclassmembers

    允許最佳化類別;如果保留類別,請保留 [{ OptionalMemberSpecification }] 中列出的成員。

    -keepnames

    允許移除類別和成員,但不要以其他方式模糊處理或修改。

    -keepclassmembernames

    允許移除類別和成員,但不要以其他方式模糊處理或修改成員。

    -keepclasseswithmembers

    如果成員符合指定模式,則不會移除或模糊處理類別。

    我們建議您盡量使用 -keepclassmembers,因為它可啟用最多最佳化功能,然後視需要使用 -keepnames-keep 不允許任何最佳化,因此請盡量少用。

  • [OptionalModifier],...] 可讓您列出類別的零個或多個 Java 語言修飾符,例如 publicfinal

  • <ClassSpecification> 可讓您指定要將保留規則套用至哪個類別 (或哪個父類別或實作的介面)。在最簡單的情況下,這會是單一完整的類別。

  • [{ OptionalMemberSpecification }] 可讓您將 Keep 行為篩選為僅符合特定模式的類別和方法。一般來說,建議在大多數保留規則中使用此選項,以免保留的資料量超出預期。

Keep 規則範例

以下列舉一些設計良好的保留規則範例。

建構子類別

這個保留規則會封裝在 androidx.room:room-runtime,以保留透過反射技術建構的資料庫建構函式。

-keep class * extends androidx.room.RoomDatabase { void <init>(); }

來自 Android 架構的反射 ObjectAnimator

這項保留規則會封裝在 androidx.vectordrawable:vectordrawable-animated,讓 ObjectAnimator 能夠透過 JNI 呼叫原生程式碼中的 getter 或 setter。請注意,Compose 中的新動畫系統不需要這類保留規則,這只是規則結構的範例。

-keepclassmembers class androidx.vectordrawable.graphics.drawable.VectorDrawableCompat$* {
   void set*(***);
   *** get*();
}

JNI 註冊

這項保留規則會封裝在 androidx.graphics:graphics-path 中,用於保留原生方法。如果程式庫是透過 env->RegisterNatives() 手動註冊原生方法,就可能需要這麼做。

-keepclasseswithmembers class androidx.graphics.path.** {
    native <methods>;
}