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

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

反射よりも codegen を優先する

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

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

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

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

最適化に関する問題を確認する

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

  • AndroidX ライブラリHilt などのライブラリは、リフレクションではなく codegen を使用するため、アプリの最適化に適しています。リフレクションを使用する場合は、必要なコードのみを保持するように最小限の保持ルールを指定します。
  • シリアル化ライブラリでは、オブジェクトのインスタンス化やシリアル化時にボイラープレート コードを回避するために、多くの場合リフレクションが使用されます。リフレクション ベースのアプローチ(JSON の Gson など)ではなく、codegen を使用してこれらの問題を回避するライブラリを探します。たとえば、代わりに Kotlin シリアル化を使用します。
  • 可能であれば、パッケージ全体の保持ルールを含むライブラリは使用しないでください。パッケージ全体の keep ルールはエラーの解決に役立ちますが、広範な keep ルールは最終的に、必要なコードのみを保持するように絞り込む必要があります。詳細については、最適化を段階的に導入するをご覧ください。

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

新しいライブラリを追加したら、最適化を有効にしてエラーがないか確認します。エラーがある場合は、そのライブラリの代替手段を探すか、保持ルールを記述します。ライブラリが最適化に対応していない場合は、そのライブラリに関するバグを報告してください。

ルールは追加方式

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

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

ライブラリがリフレクションを使用しているかどうかは、コードを調べることで判断できる場合があります。ライブラリがリフレクションを使用している場合は、関連する 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)
    

不適切な保持ルールを除外する(詳細設定)

本当に削除すべきコードを保持する 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()

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

RoomHilt はどちらもアプリ定義型を構築しますが、codegen を使用して反射の必要性を回避します。