ライブラリ作成者は、エンドユーザーに高品質なエクスペリエンスを提供しながら、アプリ デベロッパーがライブラリをアプリに簡単に組み込めるようにする必要があります。つまり、ライブラリは、デベロッパーが追加の設定を行わなくても Android の最適化(R8)と互換性があるか、Android での使用に適していない可能性があることを明記する必要があります。Android で使用するライブラリは、重要なアプリ の最適化を妨げず、追加の最適化要件に準拠することが重要です。
このドキュメントは、公開ライブラリのデベロッパーを対象としていますが、大規模なモジュール化されたアプリの内部ライブラリ モジュールのデベロッパーにも役立ちます。
アプリ デベロッパーで、Android アプリの最適化について知りたい場合は、 アプリの最適化を有効にするをご覧ください。使用するライブラリの選択については、 ライブラリを賢く選択するをご覧ください。
keep ルールの種類について
ライブラリには、次の 2 種類の keep ルールがあります。
- コンシューマ keep ルール は、ライブラリが反映するものを保持するルールを指定する必要があります。ライブラリがリフレクションまたは JNI を使用してコードやクライアント アプリで定義されたコードを呼び出す場合、これらのルールでは保持する必要があるコードを記述する必要があります。ライブラリは、アプリの keep ルールと同じ形式のコンシューマ keep ルールをパッケージ化する必要があります。これらのルールはライブラリ アーティファクト(AAR または JAR)にバンドルされ、ライブラリの使用時に Android アプリの最適化で自動的に使用されます。これらのルールは、
build.gradle.kts(またはbuild.gradle)ファイルのconsumerProguardFilesプロパティで指定された ファイルに保持されます。詳細については、その他の 推奨事項をご覧ください。 - ライブラリ ビルドの keep ルール は、ライブラリのビルド時に適用されます。ビルド時にライブラリを部分的に最適化する場合にのみ必要です。ライブラリの公開 API が削除されないようにする必要があります。削除されると、公開 API がライブラリの配布に含まれず、アプリ デベロッパーがライブラリを使用できなくなります。これらのルールは、
ファイルに保持されます。
proguardFilesプロパティで指定されたbuild.gradle.kts(またはbuild.gradle)ファイルです。詳細については、AAR ライブラリのビルドを最適化するをご覧ください。
最適化の要件とガイドライン
ライブラリの R8 構成は、使用するアプリの最終的なバイナリサイズとパフォーマンスにグローバルな影響を与えます。一般的な keep ルールのベスト プラクティスに加えて、ライブラリ作成者は特定の要件を遵守し、 追加のガイドラインを考慮する必要があります。
最適化の要件を遵守する
ライブラリの非効率性は、アプリの肥大化、メモリの浪費、起動の遅延、ANR(アプリケーション応答なしエラー)の主な原因となります。アプリの品質とユーザー エクスペリエンスを大幅に低下させないように、ライブラリは次の要件に違反しないようにする必要があります。
広範な keep ルールやパッケージ全体の keep ルールを使用しない: ライブラリには、ライブラリや別のライブラリのコードの大部分を保持する広範な keep ルールを含めないでください。広範な keep ルールは、短期的にクラッシュを解決する可能性がありますが、ライブラリを使用するすべてのアプリのアプリサイズを肥大化させます。
ライブラリや参照される他のライブラリのパッケージに、パッケージ全体の keep ルール(
-keep class com.mylibrary.** {*; }など)を含めないでください。このようなルールは、ライブラリを使用するすべてのアプリで、これらのパッケージの最適化を制限します。不適切なグローバル ルールを使用しない: グローバル オプションは、
-dontobfuscateや-allowaccessmodificationなど、絶対に使用しないでください。可能な限りリフレクションではなく codegen を使用する: 可能な場合は、リフレクションではなく コード生成(codegen)を使用します。codegen とリフレクションはどちらも、プログラミング時にボイラープレート コードを回避するための一般的なアプローチですが、codegen は R8 などのアプリ オプティマイザーとの互換性が高くなっています。
codegen では、ビルドプロセス中にコードが分析され、変更されます。 コンパイル時に大きな変更がないため、オプティマイザーは最終的に必要なコードと安全に削除できるコードを把握できます。
リフレクションでは、コードは実行時に分析および操作されます。コードは実行されるまで最終的に確定しないため、オプティマイザーは安全に削除できるコードを把握できません。実行時にリフレクションを介して動的に使用されるコードが削除される可能性が高く、ユーザーにアプリのクラッシュが発生します。
最近のライブラリの多くは、リフレクションではなく codegen を使用しています。Room、 Hilt などで使用される一般的なエントリ ポイントについては、 KSP をご覧ください。
R8 フルモードをサポートする: R8 フルモードが有効になっている場合、ライブラリがクラッシュしないようにする必要があります。R8 のフルモードは、R8 を使用する場合におすすめのモードであり、2023 年に安定版がリリースされた AGP 8.0 以降のデフォルトです。R8 でライブラリがクラッシュする場合は、パッケージ全体を保持するのではなく、特定のリフレクションまたは JNI エントリ ポイントを特定して、ターゲット ルールを追加します。
その他の推奨事項
最適化の要件に加えて、次の推奨事項があります。
- ライブラリのコンシューマ keep ルールファイルで
-repackageclassesを使用しないでください。 ただし、ライブラリのビルドを最適化するには、-repackageclasses内部パッケージ名(<your.library.package>.internalなど)を指定して ライブラリのビルド keep ルールファイルでを使用できます。これにより、最適化されていないアプリでのライブラリの効率が向上します。ただし、アプリも最適化する必要があるため、通常は必要ありません。 - ライブラリが機能するために必要な属性は、
proguard-android-optimize.txtで定義されている属性と重複する可能性がある場合でも、ライブラリの keep ルールファイルで宣言します。 - ライブラリの配布で次の属性が必要な場合は、ライブラリのコンシューマ keep ルールファイルではなく、ライブラリのビルド keep ルールファイルで保持します。
AnnotationDefaultEnclosingMethodExceptionsInnerClassesRuntimeInvisibleAnnotationsRuntimeInvisibleParameterAnnotationsRuntimeInvisibleTypeAnnotationsRuntimeVisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeVisibleTypeAnnotationsSignature
- ライブラリ作成者は、実行時にアノテーションが使用される場合は、コンシューマ keep ルールで
RuntimeVisibleAnnotations属性を保持する必要があります。 - ライブラリ作成者は、コンシューマ
keep ルールで次のグローバル オプションを使用しないでください:
-include-basedirectory-injars-outjars-libraryjars-repackageclasses-flattenpackagehierarchy-allowaccessmodification-renamesourcefileattribute-ignorewarnings-addconfigurationdebugging-printconfiguration-printmapping-printusage-printseeds-applymapping-obfuscationdictionary-classobfuscationdictionary-packageobfuscationdictionary
リフレクションが適切な場合
リフレクションを使用する必要がある場合は、次のいずれかにのみリフレクションする必要があります。
- 特定のターゲット タイプ(特定のインターフェース実装者またはサブクラス)
- 特定のランタイム アノテーションを使用するコード
このようにリフレクションを使用すると、ランタイム コストが制限され、 ターゲット コンシューマ keep ルールを作成できます。
この特定かつターゲットを絞ったリフレクションの形式は、Android フレームワーク(システム リソースの読み込み時など)と AndroidX ライブラリ(WorkManager
ListenableWorkers や RoomDatabases の構築時など)の両方で見られるパターンです。一方、Gson のオープンエンド リフレクションは、Android アプリでの使用には適していません。
よくある誤解
いくつかのよくある誤解により、R8 の構成が誤っている可能性があります。次に例を示します。
R8 の最適化の誤解: 一般的な理解とは異なり、R8 の最適化は難読化だけでなく、メソッドのインライン化とクラスのマージ手法によるコード圧縮と論理的な最適化も含まれます。詳細については、R8 の最適化 の概要をご覧ください。
難読化されたライブラリの最適化をバイパスする: ライブラリが AAR(Android アーカイブ)または JAR(Java アーカイブ)にコンパイルされたときに最適化または 難読化されたため、ライブラリを最適化から除外する一般的なエラーがあります。ライブラリのビルド時の最適化は制限されており、keep ルールに含めることでライブラリの最適化を無効にしないでください。詳細については、R8 の最適化の概要をご覧ください。
-keepオプションの誤解-keepルールを使用すると、 R8 は最適化パスを実行できなくなります。詳細については、 適切な keep オプションを選択するをご覧ください。
コンシューマ keep ルールの品質を検証する
ライブラリがアプリに埋め込まれたときにコードの最適化を過度に妨げないことを検証するには、ライブラリ サンプルまたは統合テストアプリを使用します。R8 を有効にして、R8 Configuration Analyzer でアプリの R8 構成全体の品質を検査します。
コンシューマ ルールがライブラリのコードの大部分の最適化を制限していることが判明した場合は、コンシューマ ルールを改善する必要があります。 コンシューマ ルールを絞り込み、R8 を有効にしたアプリでライブラリの影響を受ける部分をテストします。
ルールのパッケージ化を構成する
コンシューマ keep ルールが正しく適用されるようにするには、ライブラリの形式に応じて適切にパッケージ化する必要があります。
AAR ライブラリ
AAR ライブラリのコンシューマ ルールを追加するには、Android ライブラリ モジュールのビルド スクリプトで consumerProguardFiles オプションを使用します。詳細については、ライブラリ モジュールを作成する
をご覧ください。
Kotlin
android {
defaultConfig {
consumerProguardFiles("consumer-proguard-rules.pro")
}
...
}
Groovy
android {
defaultConfig {
consumerProguardFiles 'consumer-proguard-rules.pro'
}
...
}
JAR ライブラリ
JAR として配布される Kotlin または Java ライブラリにルールをバンドルするには、ルールファイルを最終的な JAR の META-INF/proguard/ ディレクトリに任意のファイル名で配置します。
たとえば、<libraryroot>/src/main/kotlin にコードがある場合は、コンシューマ
ルール ファイルを
<libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
に配置すると、ルールが出力 JAR の正しい場所にバンドルされます。
ルールが META-INF/proguard ディレクトリにあることを確認して、最終的な JAR がルールを正しくバンドルしていることを確認します。
AAR ライブラリのビルドを最適化する(詳細)
通常、ライブラリのビルド時に可能な最適化は非常に限られているため、ライブラリのビルドを直接最適化する必要はありません。ライブラリ デベロッパーは、ライブラリを最適化する前に、ライブラリとアプリのビルド時の最適化と保持動作の複数の段階について検討する必要があります。
ビルド時にライブラリを最適化する場合は、Android Gradle プラグインでサポートされています。
Kotlin
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
configureEach {
consumerProguardFiles("consumer-rules.pro")
}
}
}
Groovy
android {
buildTypes {
release {
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
configureEach {
consumerProguardFiles "consumer-rules.pro"
}
}
}
proguardFiles の動作は consumerProguardFiles と大きく異なります。
proguardFilesはビルド時に使用されます。多くの場合、getDefaultProguardFile("proguard-android-optimize.txt")と組み合わせて使用し、 ライブラリのビルド中に保持するライブラリの部分を定義します。少なくとも、これは公開 API です。- 対照的に、
consumerProguardFilesはライブラリにパッケージ化され、ライブラリを使用するアプリのビルド時に後で発生する最適化に影響します。
たとえば、ライブラリがリフレクションを使用して内部クラスを構築する場合は、proguardFiles と consumerProguardFiles の両方で keep ルールを定義する必要があります。
ライブラリのビルドで -repackageclasses を使用する場合は、ライブラリのパッケージ内のサブパッケージにクラスを再パッケージします。 たとえば、-repackageclasses 'internal' ではなく -repackageclasses
'com.example.mylibrary.internal' を使用します。
Compose UI のパフォーマンスを最適化する
ライブラリに Compose UI コンポーネントが含まれている場合は、R8 によるバイナリの縮小(前述)と再コンポーズの効率の両方を最適化します。
- 安定したコンポーザブルを設計する: 入力が変更されていない場合にコンパイラが コンポーザブルを安全にスキップできるように、安定性ルールに従ってデータモデルと ライブラリ構成を構造化します。詳細については、 Compose の安定性をご覧ください。
- コンパイラの指標を確認する: Compose コンパイラの指標を使用して、 ライブラリのエントリ ポイントが再起動可能でスキップ可能としてマークされていることを確認します。これにより、コンシューマ アプリの再コンポジションのオーバーヘッドが最小限に抑えられます。詳細については、 安定性の問題を診断するをご覧ください。
さまざまな R8 バージョンをサポートする(詳細)
ルールを調整して、特定のバージョンの R8 をターゲットに設定できます。これにより、新しい R8 バージョンを使用するプロジェクトでライブラリを最適に動作させながら、古い R8 バージョンを使用するプロジェクトで既存のルールを引き続き使用できます。
ターゲット R8 ルールを指定するには、AAR の classes.jar 内の META-INF/com.android.tools ディレクトリまたは JAR の META-INF/com.android.tools ディレクトリに含める必要があります。
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
META-INF/com.android.tools ディレクトリには、サブディレクトリが複数存在し、r8-from-<X>-upto-<Y> という形式の名前で、ルールが作成された R8 バージョンを示します。各サブディレクトリには、R8 ルールを含む 1 つ以上のファイルを含めることができます。ファイル名と拡張子は任意です。
-from-<X> と -upto-<Y> の部分は省略可能です。<Y> バージョン
は排他的です。バージョン範囲は通常は連続していますが、重複することもあります。
たとえば、r8、r8-upto-8.0.0、r8-from-8.0.0-upto-8.2.0、および
r8-from-8.2.0 は、ターゲット R8 ルールのセットを表すディレクトリ名です。r8 ディレクトリのルールは、どの R8 バージョンでも使用できます。r8-from-8.0.0-upto-8.2.0 ディレクトリのルールは、バージョン 8.0.0 から 8.2.0 未満の R8 で使用できます。
Android Gradle プラグインは、その情報を使用して、現在の R8 バージョンで使用できるすべてのルールを選択します。ライブラリでターゲット R8
ルールが指定されていない場合、Android Gradle プラグインはレガシーの場所
(proguard.txt(AAR の場合)またはMETA-INF/proguard/<ProGuard-rule-files>(
JAR の場合))からルールを選択します。