ライブラリを賢く選択する

アプリの最適化を有効にするには、Android の最適化と互換性のあるライブラリを使用する必要があります。ライブラリが Android の最適化用に構成されていない場合(たとえば、関連する keep ルールをバンドルせずに リフレクション を使用している場合)、Android アプリには適していない可能性があります。このページでは、一部のライブラリがアプリの最適化に適している理由と、選択に役立つ一般的なヒントについて説明します。

ライブラリを選択する際の一般的なヒント

ライブラリがアプリの最適化と互換性があることを確認するには、次のヒントを参考にしてください。

リフレクションよりもコード生成を優先する

リフレクションではなくコード生成(codegen を使用するライブラリを選択します。codegen を使用すると、オプティマイザーは実行時に実際に使用されるコードと削除できるコードを判別できます。ライブラリが codegen を使用しているかリフレクションを使用しているかを判断するのは難しい場合がありますが、いくつかの兆候があります。ヒントをご覧ください。

codegen とリフレクションの詳細については、ライブラリ作成者向けの 最適化をご覧ください。

リフレクションの使用を確認する(上級)

ライブラリがリフレクションを使用しているかどうかは、そのコードを調べることで判断できます。 ライブラリがリフレクションを使用している場合は、関連する keep ルールが提供されていることを確認してください。ライブラリが次のことを行っている場合、リフレクションを使用している可能性があります。

  • 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)
    

最適化の問題を確認する

新しいライブラリを検討する際は、ライブラリの Issue Tracker やオンライン ディスカッションで、縮小やアプリの最適化の構成に関連する問題がないかどうかを確認してください。問題がある場合は、そのライブラリの代替を探す必要があります。次の点に注意してください。

  • AndroidX ライブラリHilt などのライブラリは、リフレクションではなく主に codegen を使用するため、 アプリの最適化に適しています。リフレクションを使用する場合は、必要なコードのみを保持する最小限の keep ルールを提供します。
  • シリアル化ライブラリは、オブジェクトのインスタンス化やシリアル化の際にボイラープレート コードを回避するために、リフレクションを頻繁に使用します。リフレクション ベースの アプローチ(JSON 用の Gson など)ではなく、codegen を使用して これらの問題を回避するライブラリを探します。たとえば、Kotlin シリアル化 {:.external} または codegen を使用した Moshi を使用します。
  • 可能であれば、パッケージ全体の keep ルールを含むライブラリは避けてください。 パッケージ全体の keep ルールはエラーの解決に役立ちますが、広範な keep ルールは、必要なコードのみを保持するように最終的に絞り込む必要があります。詳細については、最適化を段階的に導入するをご覧ください。
  • サードパーティ ライブラリを使用するアプリを公開する前に、R8 Configuration Analyzer を使用して、提供されている keep ルールを監査します。レポートを確認することで、ライブラリの keep ルールが広すぎるために、R8 がコードベースで重要な最適化を実行できないかどうかを確認できます。このチェックにより、選択したライブラリがアプリのパフォーマンス目標に沿っており、不要な構成の肥大化を招かないようにします。
  • ライブラリでは、ドキュメントからプロジェクト内のファイルに keep ルールをコピーして貼り付ける必要はありません。特に、パッケージ全体の keep ルールは避けてください。これらのルールは、長期的にアプリ デベロッパーのメンテナンスの負担となり、最適化や変更が難しくなります。

新しいライブラリを追加したら最適化を有効にする

新しいライブラリを追加したら、後で最適化を有効にして、エラーがないかどうかを確認します。エラーがある場合は、そのライブラリの代替を探すか、keep ルールを作成します。ライブラリが最適化に対応していない場合は、そのライブラリにバグを報告してください。

不適切な keep ルールを除外する(上級)

keep ルールは付加的なルールです。つまり、ライブラリの依存関係に含まれる特定のルールは削除できず、アプリの他の部分のコンパイルに影響を及ぼす可能性があります。たとえば、コードの最適化を無効にするルールがライブラリに含まれている場合、そのルールによってプロジェクト全体の最適化が無効になります。

削除すべきコードを保持する keep ルールを含むライブラリは避ける必要があります。ただし、使用する必要がある場合は、次のコードに示すようにルールを除外できます。

// 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 オブジェクトのリストを取得する場合、コンストラクタを呼び出したり、Factory を 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()

Gson での R8 の動作については、Gson コンシューマ ルールをご覧ください。R8 がこのコードを分析し、UserList または User がどこにもインスタンス化 されていない場合、フィールドの名前を変更したり、使用されていないと思われる コンストラクタを削除したりして、アプリがクラッシュする可能性があります。他のライブラリを同様の方法で使用している場合は、アプリの最適化を妨げないことを確認し、妨げる場合は使用しないでください。

新しいバージョンの Gson で @SerializedName を使用する

Gson のコンシューマ ルールと互換性のある方法でデータモデルを定義するには、次のスニペットに示すように、フィールドに @SerializedName アノテーションを付けます。

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

@SerializedName アノテーションを使用すると、R8 はモデル クラスを Gson バージョン 2.11.0 以降にバンドルされている keep ルールと照合できます。 R8 は、アノテーション付きのフィールドと必要なコンストラクタを自動的に保持するため、プロジェクトで ProGuard 構成を手動で管理しなくても、ライブラリのバンドルされたルールに依存できます。

RoomHiltcodegen を使用した Moshi は アプリ定義の型を構築しますが、codegen を使用してリフレクションの必要性を回避します。