アプリの最適化を有効にするには、Android の最適化と互換性のあるライブラリを使用する必要があります。ライブラリが Android の最適化用に構成されていない場合(関連する保持ルールをバンドルせずにリフレクションを使用している場合など)、Android アプリには適していない可能性があります。このページでは、一部のライブラリがアプリの最適化に適している理由と、選択に役立つ一般的なヒントについて説明します。
リフレクションよりもコード生成を優先する
一般に、リフレクションではなくコード生成(codegen)を使用するライブラリを選択する必要があります。codegen を使用すると、オプティマイザーは、実行時に実際に使用されるコードと削除できるコードをより簡単に判断できます。ライブラリがコード生成を使用しているかリフレクションを使用しているかを判断するのは難しい場合がありますが、いくつかの兆候があります。詳しくは、ヒントをご覧ください。
codegen とリフレクションの詳細については、ライブラリ作成者向けの最適化をご覧ください。
ライブラリを選択する際の一般的なヒント
以下のヒントを参考に、ライブラリがアプリの最適化に対応していることを確認してください。
最適化の問題を確認する
新しいライブラリを検討する際は、ライブラリの Issue トラッカーとオンライン ディスカッションを確認して、縮小化やアプリの最適化の構成に関連する問題がないかどうかを確認してください。ある場合は、そのライブラリの代替を探す必要があります。次の点に注意してください。
- AndroidX ライブラリや Hilt などのライブラリは、リフレクションではなくコード生成を使用するため、アプリの最適化にうまく対応できます。リフレクションを使用する場合は、必要なコードのみを保持する最小限の保持ルールを指定します。
- シリアル化ライブラリでは、オブジェクトのインスタンス化やシリアル化の際にボイラープレート コードを回避するために、リフレクションがよく使用されます。リフレクション ベースのアプローチ(JSON の Gson など)の代わりに、codegen を使用してこれらの問題を回避するライブラリを探します。たとえば、代わりに Kotlin シリアル化を使用します。
- パッケージ全体の保持ルールを含むライブラリは、可能な限り避けるべきです。パッケージ全体の保持ルールはエラーの解決に役立ちますが、最終的には必要なコードのみを保持するように広範な保持ルールを絞り込む必要があります。詳細については、最適化を段階的に導入するをご覧ください。
- ライブラリでは、ドキュメントからプロジェクト内のファイルに保持ルールをコピーして貼り付ける必要はありません。特に、パッケージ全体の保持ルールは必要ありません。これらのルールは、長期的にアプリ デベロッパーのメンテナンスの負担となり、時間の経過とともに最適化や変更が難しくなります。
新しいライブラリを追加した後に最適化を有効にする
新しいライブラリを追加したら、最適化を有効にして、エラーがないかどうかを確認します。エラーがある場合は、そのライブラリの代替手段を探すか、保持ルールを作成します。ライブラリが最適化に対応していない場合は、そのライブラリに関するバグを報告してください。
ルールは追加方式で運用されます
保持ルールは加算式で運用されることに注意してください。つまり、ライブラリの依存関係に含まれている特定のルールは削除できず、アプリの他の部分のコンパイルに影響する可能性があります。たとえば、コードの最適化を無効にするルールがライブラリに含まれている場合、そのルールによってプロジェクト全体の最適化が無効化されます。
リフレクションの使用を確認する(高度な方法)
ライブラリがリフレクションを使用しているかどうかは、そのコードを検査することで判断できる場合があります。ライブラリがリフレクションを使用している場合は、関連する保持ルールが提供されていることを確認します。ライブラリが次の処理を行っている場合、リフレクションを使用している可能性があります。
kotlin.reflectパッケージまたはjava.lang.reflectパッケージのクラスまたはメソッドを使用するClass.forName関数またはclassLoader.getClass関数を使用する- 実行時にアノテーションを読み取ります。たとえば、
val value = myClass.getAnnotation()またはval value = myMethod.getAnnotation()を使用してアノテーション値を保存し、valueで何かを行う場合などです。 メソッド名を文字列として使用してメソッドを呼び出します。例:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
不適切な保持ルールを除外する(詳細設定)
削除されるべきコードを保持するキープルールを含むライブラリは避けるべきです。ただし、どうしても使用する必要がある場合は、次のコードに示すようにルールをフィルタリングできます。
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
ケーススタディ: 最適化によって Gson が破損する理由
Gson は、リフレクションを多用するため、アプリの最適化で問題が発生することが多いシリアル化ライブラリです。次のコード スニペットは、Gson の一般的な使用方法を示しています。この使用方法では、実行時にクラッシュが発生しやすくなります。Gson を使用して User オブジェクトのリストを取得する場合、コンストラクタを呼び出したり、ファクトリを fromJson() 関数に渡したりしないことに注意してください。次のいずれもなしにアプリ定義クラスを構築または使用している場合、ライブラリがオープンエンド リフレクションを使用している可能性があります。
- ライブラリ、標準インターフェース、クラスを実装するアプリクラス
- KSP などのコード生成プラグイン
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
R8 がこのコードを分析し、UserList または User がどこでもインスタンス化されていないと判断した場合、フィールドの名前を変更したり、使用されていないと思われるコンストラクタを削除したりして、アプリがクラッシュする可能性があります。他のライブラリを同様の方法で使用している場合は、アプリの最適化を妨げないことを確認し、妨げる場合は使用を避ける必要があります。
Room と Hilt はどちらもアプリ定義型を構築しますが、リフレクションの必要性を回避するためにコード生成を使用します。