プロダクト ニュース

R8 の Keep ルールを構成してトラブルシューティングする

7 分で読了
Ajesh Pai & Ben Weiss

最新の Android 開発では、小さく、高速で、安全なアプリケーションをリリースすることがユーザーの基本的な期待となっています。これを実現するための Android ビルドシステムの主要なツールは、R8  最適化ツールです。これは、圧縮のための不要なコードとリソースの削除、コードの名前変更または最小化、アプリの最適化を処理するコンパイラです。

R8 を有効にすることは、アプリをリリースする準備の重要なステップですが、デベロッパーが「Keep ルール」の形式でガイダンスを提供する必要があります。

この記事を読んだら、YouTube で公開されている Performance Spotlight Week の動画で、R8 最適化ツールの有効化、デバッグ、トラブルシューティングについてご確認ください。

 

 

Keep ルールが必要な理由

Keep ルールを記述する必要があるのは、R8 が静的解析ツールであるのに対し、Android アプリは多くの場合、リフレクションや JNI(Java Native Interface)を使用したネイティブ コードとの呼び出しなど、動的な実行パターンに依存しているという根本的な矛盾があるためです。

R8 は、直接呼び出しを分析して、使用されているコードのグラフを構築します。コードが動的にアクセスされる場合、R8 の静的解析ではそれを予測できず、そのコードは 未使用と識別されて削除され、ランタイム クラッシュが発生します。

Keep ルール は、R8 コンパイラに対する明示的な指示です。これは、「この特定のクラス、メソッド、またはフィールドは、実行時に動的にアクセスされるエントリ ポイントです。」と述べています。「この特定のクラス、メソッド、フィールドは、ランタイム時に動的にアクセスされるエントリ ポイントです。直接参照が見つからない場合でも、保持する必要があります。」

Keep ルールの詳細については、公式ガイドをご覧ください。

Keep ルールを記述する場所

アプリケーションのカスタム Keep ルールは、テキスト ファイルに記述します。慣例により、このファイルは proguard-rules.pro という名前で、アプリまたはライブラリ モジュールのルートに配置されます。このファイルは、モジュールの build.gradle.kts ファイルの release ビルドタイプで指定します。

release {

    isShrinkResources = true

    isMinifyEnabled = true

    proguardFiles(

        getDefaultProguardFile("proguard-android-optimize.txt"),

        "proguard-rules.pro",

    )

}

正しいデフォルト ファイルを使用する

getDefaultProguardFile メソッドは、Android SDK によって提供されるデフォルトのルールセットをインポートします。間違ったファイルを使用すると、アプリが最適化されない可能性があります。proguard-android-optimize.txt を使用してください。このファイルは、標準の Android コンポーネントのデフォルトの Keep ルールを提供し、and R8 のコード最適化を有効にします。古い proguard-android.txt は Keep ルールのみを提供し、R8 の最適化を 有効にしません

progaurd.png

これは深刻なパフォーマンスの問題であるため、Android Studio Narwhal 3 Feature Drop 以降では、間違ったファイルを使用しているデベロッパーに警告が表示されるようになります。また、Android Gradle プラグイン バージョン 9.0 以降では、古い proguard-android.txt ファイルはサポートされなくなります。最適化されたバージョンにアップグレードしてください。

Keep ルールの記述方法

Keep ルールは、次の 3 つの主要な部分で構成されます。

  1. -keep -keep-keepclassmembers-keepclassmembers などの**オプション**
  2. allowshrinking などの省略可能な修飾子
  3. 一致させるコードを定義するクラス仕様

完全な構文と例については、Keep ルールを追加する手順をご覧ください。

Keep ルールのアンチパターン

ベスト プラクティスだけでなく、アンチパターンについても知っておくことが重要です。これらのアンチパターンは、誤解やトラブルシューティングのショートカットから発生することが多く、本番環境のビルドのパフォーマンスに壊滅的な影響を与える可能性があります。

グローバル オプション

これらのフラグはグローバル トグルであり、リリースビルドでは絶対に使用しないでください 。問題の切り分けのための一時的なデバッグにのみ使用します。

-dontotptimize を使用すると、R8 のパフォーマンス最適化が無効になり、アプリの速度が低下します。

-dontobfuscate を使用すると、すべての名前変更が無効になり、-dontshrink を使用すると、不要なコードの削除が無効になります。どちらのグローバル ルールもアプリのサイズを大きくします。

パフォーマンスの高いアプリのユーザー エクスペリエンスを実現するため、可能な限り本番環境でこれらのグローバル フラグを使用しないようにしてください。

制限が過度に緩い Keep ルール

R8 のメリットを無効にする最も簡単な方法は、制限が過度に緩い Keep ルールを記述する ことです。次のような Keep ルールは、このパッケージまたはそのサブパッケージのすべてのクラスを圧縮、難読化、最適化しないように R8 最適化ツールに指示します。これにより、そのパッケージ全体の R8 のメリットが完全に失われます。代わりに、制限が厳しく具体的な Keep ルールを記述してください。

-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS

反転演算子(!)

反転演算子(!)は、ルールからパッケージを除外する強力な方法のように思えますが、そう簡単ではありません。例を見てみましょう

-keep class !com.example.my_package.** { *; } // USE WITH CAUTION

このルールは「 のクラスを保持しないcom.example.package」という意味だと思われるかもしれませんが、実際には「アプリケーション全体のすべてのクラス、メソッド、プロパティ  com.example.packageにないものを保持する」という意味になります。驚いた場合は、R8 構成で否定がないか確認してください。

Android コンポーネントの冗長なルール

もう 1 つのよくある間違いは、アプリの ActivitiesServicesBroadcastReceivers の Keep ルールを手動で追加することです。これは不要 です。デフォルトの proguard-android-optimize.txt ファイルには、これらの標準の Android コンポーネントがすぐに動作するための関連ルールがすでに含まれています。

また、多くのライブラリには独自の Keep ルールが用意されています。そのため、これらのルールを自分で記述する必要はありません。使用しているライブラリの Keep ルールに問題がある場合は、ライブラリの作成者に連絡して問題を確認することをおすすめします。

Keep ルールのベスト プラクティス

やってはいけないことがわかったので、ベスト プラクティスについて説明します。

制限が厳しく具体的な Keep ルールを記述する

優れた Keep ルールは、できるだけ制限が厳しく具体的 である必要があります。必要なものだけを保持し、それ以外のすべてを R8 で最適化できるようにする必要があります。

ルール品質

 

-keep class com.example.** { ; }

 

低: パッケージとそのサブパッケージ全体を保持します

 

-keep class com.example.MyClass { ; }

 

低: クラス全体を保持します。これはまだ広すぎる可能性があります
-keepclassmembers class com.example.MyClass {

    private java.lang.String secretMessage;

    public void onNativeEvent(java.lang.String);

}
高: 特定のクラスの関連するメソッドとプロパティのみが保持されます

共通の祖先を使用する

複数の異なるデータモデルに対して個別の Keep ルールを記述するのではなく、共通の基本クラスまたはインターフェースをターゲットとするルールを 1 つ記述します。次のルールは、このインターフェースを実装するクラスのメンバーを保持するように R8 に指示し、拡張性が高くなります。

# Keep all fields of any class that implements SerializableModel

-keepclassmembers class * implements com.example.models.SerializableModel {

    <fields>;

}

アノテーションを使用して複数のクラスをターゲットにする

カスタム アノテーション(@Serialize など)を作成し、フィールドを保持する必要があるクラスに「タグ付け」します。これは、クリーンで宣言的で、拡張性の高いパターンです。使用しているフレームワークの既存のアノテーションに対して Keep ルールを作成することもできます。

# Keep all fields of any class annotated with @Serialize

-keepclassmembers class * {

    @com.example.annotations.Serialize <fields>;

}

適切な Keep オプションを選択する

Keep オプションは、ルールの最も重要な部分です。間違ったオプションを選択すると、最適化が不要に無効になる可能性があります。

Keep オプション動作
-keep宣言で言及されているクラスとメンバー が削除または名前変更されないようにします。
-keepclassmembers指定されたメンバーが削除または名前変更されないようにしますが、クラス自体は削除できます。ただし、削除されないクラスに限ります。
-keepclasseswithmembers組み合わせ: 指定されたすべてのメンバーが存在する場合にのみ、クラス そのメンバーを保持します。

Keep オプションの詳細については、Keep オプションのドキュメントをご覧ください。

修飾子を使用して最適化を許可する

allowshrinkingallowobfuscation などの修飾子は、広範な -keep ルールを緩和し、最適化の権限を R8 に戻します。たとえば、レガシー ライブラリでクラス全体に -keep を使用する必要がある場合は、圧縮と難読化を許可することで、最適化の一部を取り戻すことができます。

# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it.

-keep,allowshrinking,allowobfuscation class com.example.LegacyClass

追加の最適化のためのグローバル オプションを追加する

Keep ルールに加えて、R8 構成ファイルにグローバル フラグを追加して、さらに最適化を促すことができます。

-repackageclasses は、難読化されたすべてのクラスを 1 つのパッケージに移動するように R8 に指示する強力なオプションです。これにより、冗長なパッケージ名文字列が削除され、DEX ファイルの容量が大幅に節約されます。

-allowaccessmodification を使用すると、R8 はアクセス権を拡大(private から public など)して、より積極的なインライン化を有効にできます。これは、proguard-android-optimize.txt を使用する場合にデフォルトで有効になっています。

警告: ライブラリの作成者は、これらのグローバル最適化フラグをコンシューマー ルールに追加しないでください。追加すると、アプリ全体に強制的に適用されます。

さらに明確にするため、Android Gradle プラグイン バージョン 9.0 では、ライブラリのグローバル最適化フラグを完全に無視するようになります。

ライブラリのベスト プラクティス

すべての Android アプリは、何らかの方法でライブラリに依存しています。ライブラリのベスト プラクティスについて説明します。

ライブラリ デベロッパー向け

ライブラリでリフレクションまたは JNI を使用する場合は、必要な Keep ルールをコンシューマーに提供する必要があります。これらのルールは consumer-rules.pro ファイルに配置され、ライブラリの AAR ファイルに自動的にバンドルされます。

android {

    defaultConfig {

        consumerProguardFiles("consumer-rules.pro")

    }

    ...

}

ライブラリ コンシューマー向け

問題のある Keep ルールを除外する

問題のある Keep ルールを含むライブラリを使用する必要がある場合は、AGP 9.0 以降で build.gradle.kts ファイルで除外できます。これにより、R8 は特定の依存関係からのルールを無視します。

release {

    optimization.keepRules {

        // Ignore all consumer rules from this specific library

        it.ignoreFrom("com.somelibrary:somelibrary")

    }

}

最適な Keep ルールは Keep ルールがないこと

R8 構成の最終的な戦略は、Keep ルールを記述する必要性を完全になくす ことです。多くのアプリでは、リフレクションよりもコード生成 を優先する最新のライブラリを選択することで実現できます。コード生成を使用すると、最適化ツールは実行時に実際に使用されるコードと削除できるコードをより簡単に判断できます。また、動的なリフレクションを使用しないということは、「隠れた」エントリ ポイントがないため、Keep ルールは不要です。 新しいライブラリを選択する場合は、常にリフレクションよりもコード生成を使用するソリューションを選択してください。

ライブラリの選択方法について詳しくは、ライブラリを賢く選択するをご覧ください。

R8 構成のデバッグとトラブルシューティング

R8 が保持すべきコードを削除した場合や、APK が想定よりも大きい場合は、次のツールを使用して問題を診断します。

重複する Keep ルールとグローバル Keep ルールを見つける

R8 は数十のソースからルールをマージするため、「最終的な」ルールセットを把握するのは困難な場合があります。このフラグを proguard-rules.pro ファイルに追加すると、完全なレポートが生成されます。

# Outputs the final, merged set of rules to the specified file

-printconfiguration build/outputs/logs/configuration.txt

このファイルを検索して、冗長なルールを見つけたり、問題のあるルール(-dontoptimize など)をそのルールを含む特定のライブラリにトレースしたりできます。

R8 に質問する: なぜこれを保持するのですか?

削除されるはずのクラスがアプリに残っている場合は、R8 に理由を尋ねることができます。次のルールを追加します。

# Asks R8 to explain why it's keeping a specific class

class com.example.MyUnusedClass

-whyareyoukeeping 

ビルド中に、R8 はそのクラスを保持した原因となった参照の正確なチェーンを出力します。これにより、参照をトレースしてルールを調整できます。

完全なガイドについては、R8 のトラブルシューティングをご覧ください。

次のステップ

R8 は、Android アプリのパフォーマンスを向上させる強力なツールです。その有効性は、静的解析エンジンとしての動作を正しく理解しているかどうかにかかっています。

具体的なメンバーレベルのルールを記述し、祖先とアノテーションを活用し、適切な Keep オプションを慎重に選択することで、必要なものだけを保持できます。最も高度な方法は、リフレクション ベースの前身ではなく、最新のコード生成ベースのライブラリを選択することで、ルールを完全に不要にすることです。

Performance Spotlight Week に沿って、YouTube で公開されている今日の Spotlight Week の動画を確認し、R8 チャレンジを続けてください。R8 の有効化またはトラブルシューティングに関するご質問は、#optimizationEnabled を使用してください。サポートさせていただきます。

特典を実感してください。

今日、アプリで R8 フルモードを有効にすることをおすすめします今日

  1. デベロッパー ガイドに沿って、アプリの最適化を有効にします
  2. proguard-android.txt をまだ使用している場合は、proguard-android-optimize.txt に置き換えます。
  3. 次に、影響を測定します違いを感じるだけでなく、検証してください。 GitHub の Macrobenchmark サンプルアプリ のコードを適応させて、起動時間を測定し、パフォーマンスの向上を測定します。

アプリのパフォーマンスが大幅に向上すると確信しています。

その際、ソーシャル タグ #AskAndroid を使用して質問を投稿してください。1 週間を通して、Google のエキスパートが質問をモニタリングして回答します。

明日は、ベースライン プロファイルとスタートアップ プロファイルを使用したプロファイル ガイド付き最適化について説明します。また、過去のリリースで Compose レンダリングのパフォーマンスがどのように改善されたか、バックグラウンド作業のパフォーマンスに関する考慮事項についても説明します。

執筆者:

続きを読む