保持ルールについて

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

keep ルールが必要な一般的なシナリオ

R8 は、コード内のすべての直接呼び出しを特定して保持します。ただし、R8 は間接的なコードの使用を認識できないため、アプリに必要なコードが削除されてクラッシュが発生する可能性があります。keep ルールを使用して、間接的に使用されるコードを保持するように R8 に指示します。keep ルールが必要になる一般的な状況は次のとおりです。

  • リフレクションによってアクセスされるコード: R8 は、クラス、フィールド、メソッドがリフレクションによってアクセスされるタイミングを特定できません。たとえば、R8 は、Class.getDeclaredMethod() を使用して名称でルックアップされたメソッドや、Class.getAnnotation() で取得されたアノテーションを識別できません。このような場合、R8 はこれらのメソッドとアノテーションの名前を変更するか、完全に削除する可能性があるため、実行時に ClassNotFoundException または NoSuchMethodException が発生します。
  • Java Native Interface(JNI)から呼び出されるコード: ネイティブ(C または C++)コードが Java または Kotlin メソッドを呼び出す場合、または Java または Kotlin コードが JNI を使用して C++ コードを呼び出す場合、呼び出しはメソッド名の動的な文字列検索に基づきます。R8 は動的な文字列ベースのメソッド呼び出しを認識できないため、最適化によってコードが破損する可能性があります。

これは keep ルールが必要なシナリオの完全なリストではありませんが、これらのシナリオは keep ルールが必要になる可能性のあるほとんどのケースを網羅しています。

独自のコードでリフレクションを使用している場合や、JNI から呼び出される場合は、keep ルールを手動で作成する代わりに、Jetpack Keep アノテーションを使用してそのコードにアノテーションを付けることができます。その Gradle プラグインは、ビルド時にアノテーションから keep ルールを生成します。

保持ルールが最適化に与える影響を理解する

R8 は、アプリコードを分析して、未使用のコードの削除、メソッドのインライン化、クラスの統合、識別子の難読化を行う、プログラム全体のオプティマイザーです。ただし、R8 が最適化できるのは、定義した構成内に限られます。

keep ルールは、最適化を制限するための明示的な指示を表します。宣言した keep ルールは、コードベースのその部分を変換する R8 の機能を制限します。適切に作成された保持ルールの価値は、その具体性にあります。

  • 範囲を絞った keep ルールを使用すると、最適化を最大限に活用できます。動的にアクセスされる正確なエントリ ポイント(リフレクション コンストラクタやシリアル化されたフィールドなど)のみをターゲットにすることで、R8 でコードの残りの部分を最適化できます。

  • 広範な keep ルールは最適化を妨げる: パッケージ全体のルールや広範なワイルドカードを宣言すると、R8 はクラス全体とそのすべてのメンバーを最適化できなくなります。R8 は、そのコードを削除、インライン化、マージ、難読化できません。

過度に広範な keep ルールは、DEX バイナリの肥大化、アプリのメモリ使用量の増加、起動時間の遅延(TTID と TTFD)、ANR 発生率の上昇に直接つながります。目標は、クラスを「念のため」保持することではなく、動的実行に必要な最小限のコントラクトを定義することです。

サードパーティ ライブラリのルールなど、広範な keep ルールや不要な keep ルールを見つけるには、R8 Configuration Analyzer を使用します。R8 が最適化できるコードの量を示す、圧縮、最適化、難読化のスコアがレポートされます。

アプリに keep ルールを追加する方法

保持ルールの構成は、使用する AGP のバージョンによって異なります。

大規模なアプリでは、通常、複数のライブラリ モジュールにコードが含まれています。このような場合は、特定のライブラリ モジュール内で、適用されるコードの横に keep ルールを配置する方が望ましいことがよくあります。ライブラリの keep ルールの維持における重要な違いは、ライブラリ モジュールの build.gradle.kts(または build.gradle)ファイル内でこれらのルールを宣言する方法にあります。詳しくは、ライブラリ作成者向けの最適化をご覧ください。

AGP バージョン 9.3 以降の場合

src/<variant>/keepRules ディレクトリのサフィックス .keep を含むファイルにルールを追加します。例: src/main/keepRules/custom-rules.keep。AGP 9.3 以降で更新された DSL を使用すると、コードとリソースの両方の最適化が有効になります。また、デフォルトの Android 保持ルールファイル(proguard-android-optimize.txt)を明示的に定義する必要はありません。このファイルは、明示的に省略されていない限り、デフォルトで含まれているためです。

Kotlin

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
            }
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
            }
        }
    }
}

デフォルトの Android プラットフォーム保持ルールを省略する(上級者向け)

AGP には、一連のデフォルトの Android プラットフォーム ルールが用意されています。これらのルールをプロジェクトの構成から除外し、すべての保持ルールを自分で管理するには、optimization.keepRules.includeDefault を false に設定します。

Kotlin

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
                keepRules {
                    includeDefault = false // The file with optimization
                    // rules for the Android platform is included by
                    // default. To omit this file, set this option to false.
                }
            }
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
                keepRules {
                    includeDefault = false // The file with optimization
                    // rules for the Android platform is included by
                    // default. To omit this file, set this option to false.
                }
            }
        }
    }
}

9.3 より前の AGP バージョンのレガシー DSL

アプリ モジュールのルート ディレクトリにある proguard-rules.pro ファイルにルールを追加します。このファイルがまだ存在しない場合は、作成します。ファイル内のルールを適用するには、モジュール レベルの build.gradle.kts(または build.gradle)ファイルでファイルを宣言する必要があります。

デフォルトでは、ビルドファイルには proguard-android-optimize.txt ファイルも含まれています。このファイルには、ほとんどの Android プロジェクトに必要なルールが含まれているため、ビルドファイルに残しておく必要があります。このファイルは proguard-common.txt ファイルに基づいており、コンテンツを共有しています。

以前の DSL(Kotlin)

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

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

以前の DSL(Groovy)

android {
    buildTypes {
        release {
            minifyEnabled = true
            shrinkResources = true

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

keep ルールを追加する

keep ルールを追加する際は、グローバル オプションを含めることも、独自の keep ルールを定義することもできます。

  • グローバル オプション: グローバル オプションは、コードベース全体での R8 の動作に影響する一般的なディレクティブです。詳細については、グローバル オプションをご覧ください。

  • keep ルール: keep ルールは、アプリを誤って破損させることなくコードの最適化を最大限に高める適切なバランスを確保するために、慎重に設計する必要があります。keep ルールの作成方法については、keep ルールを追加するをご覧ください。

ライブラリ作成者向けの keep ルール

グローバル オプションと keep ルールの構文について学習したら、ライブラリ作成者向けの最適化で詳細を確認してください。