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

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

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

以下のヒントを参考に、ライブラリがアプリの最適化に対応していることを確認してください。

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

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

コード生成とリフレクションの詳細については、ライブラリ作成者のための最適化をご覧ください。

リフレクションの使用を確認する(高度な設定)

ライブラリがリフレクションを使用しているかどうかは、そのコードを検査することで確認できます。ライブラリがリフレクションを使用している場合は、関連する保持ルールが提供されていることを確認します。ライブラリが次の処理を行う場合、リフレクションを使用している可能性があります。

  • 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 トラッカーとオンライン ディスカッションを確認して、縮小化やアプリの最適化の構成に関連する問題がないかどうかを確認してください。ある場合は、そのライブラリの代替を探す必要があります。次の点に注意してください。

  • AndroidX ライブラリHilt などのライブラリは、リフレクションではなく主にコード生成を使用するため、アプリの最適化に適しています。リフレクションを使用する場合は、必要なコードのみを保持する最小限の保持ルールを提供します。
  • シリアル化ライブラリでは、オブジェクトのインスタンス化やシリアル化の際にボイラープレート コードを回避するために、リフレクションがよく使用されます。リフレクション ベースのアプローチ(JSON の Gson など)ではなく、codegen を使用してこれらの問題を回避するライブラリを探します。たとえば、Kotlin シリアル化codegen を使用した Moshi などがあります。
  • 可能であれば、パッケージ全体の保持ルールを含むライブラリは避けてください。パッケージ全体の 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 オブジェクトのリストを取得する場合、コンストラクタを呼び出したり、ファクトリを 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 のコンシューマー ルールと互換性のある方法でクラスを定義するには、次のスニペットを参考にしてください。

class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)

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