保持ルールを追加する

デフォルト設定でアプリの最適化を有効にすると、R8 はパフォーマンスのメリットを最大化するために広範な最適化を行います。R8 は、クラスのフィールドやメソッドの名前変更、移動、削除など、コードに大幅な変更を加えます。これによりエラーが発生した場合は、keep ルールを記述して、変更しないコードの部分を指定する必要があります。

次のような状況では、R8 によってコードが誤って削除または変更される可能性があります。

  • リフレクション: Class.forName()Method.invoke() など、リフレクションを使用してアクセスされるコード。通常、R8 は、この方法でアクセスされるクラスまたはメソッドを特定できません。
  • シリアル化: シリアル化とシリアル化解除に必要なクラスまたはフィールドが、R8 では未使用と見なされる場合があります(これはリフレクションの別の形態です)。
  • Java Native Interface(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 プロジェクトに必要なルールが含まれているため、ビルド スクリプトに保持する必要があります。

保持ルールを作成する方法

アプリのコンパイル中に、R8 はアプリの呼び出しグラフを分析して、アプリに保持する必要があるコードを検出します。このグラフは、マニフェスト エントリ(アクティビティやサービスなど)から始まり、すべてのアプリとライブラリ関数呼び出しをトレースします。R8 は、この方法で直接参照されていないコードを削除します。実行されるコードがこのグラフの一部でない場合(リフレクションによって呼び出されるコードなど)は、問題が発生する可能性があります。独自の keep ルールを記述することで、アプリに残す必要があるコードを R8 に通知できます。

保持ルールを追加するには、proguard-rules.pro ファイルに -keep 行を追加します。

ルールの構文を保持する

通常、Keep ルールの形式は次のとおりです。

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

たとえば、特定のクラスとそのすべてのメンバーを保持するには、次のようにします。

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

その他の例については、例のセクションをご覧ください。

保持ルールのコンポーネントは次のとおりです。

  • <KeepOption> を使用すると、クラスのどの部分を保持するかを指定できます。

    保持オプション 説明

    -keep

    [{ OptionalMemberSpecification }] にリストされているクラスとメンバーを保持します。

    -keepclassmembers

    クラスの最適化を許可します。クラスが保持される場合は、[{ OptionalMemberSpecification }] にリストされているメンバーを保持します。

    -keepnames

    クラスとメンバーの削除を許可しますが、難読化やその他の方法による変更は許可しません。

    -keepclassmembernames

    クラスとメンバーの削除を許可しますが、メンバーを難読化したり、他の方法で変更したりしないでください。

    -keepclasseswithmembers

    メンバーが指定されたパターンと一致する場合、クラスの削除や難読化は行われません。

    ほとんどの場合、-keepclassmembers を使用することをおすすめします。これは、最も多くの最適化を有効にできるためです。必要に応じて -keepnames を使用します。-keep では最適化が許可されないため、使用は控えてください。

  • [OptionalModifier],...] を使用すると、クラスの Java 言語修飾子(publicfinal など)を 0 個以上一覧表示できます。

  • <ClassSpecification> を使用すると、keep ルールを適用するクラス(またはスーパークラスまたは実装されたインターフェース)を指定できます。最も単純なケースでは、これは 1 つの完全修飾クラスです。

  • [{ OptionalMemberSpecification }] を使用すると、特定のパターンに一致するクラスとメソッドのみに保持動作をフィルタできます。通常、意図した以上に保持されないように、ほとんどの保持ルールでこの方法が推奨されます。

Keep ルールの例

適切に設計された保持ルールの例を次に示します。

サブクラスを作成する

この保持ルールは、データベース コンストラクタをリフレクションによってインスタンス化するために、androidx.room:room-runtime 内にパッケージ化されています。

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

Android フレームワーク ObjectAnimator からのリフレクション

この keep ルールは androidx.vectordrawable:vectordrawable-animated 内にパッケージ化されており、ObjectAnimator が JNI を使用してネイティブ コードのゲッターまたはセッターに対して呼び出しを行うことができます。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>;
}