Skip to content

Most visited

Recently visited

navigation

64K を超えるメソッドを使用するアプリの設定

Android プラットフォームの継続的な拡張に伴い、Android アプリのサイズも拡大しています。アプリおよびアプリで参照するライブラリが特定のサイズに達すると、Android アプリのビルド アーキテクチャ制限に達したことを示すビルドエラーが発生します。以前のバージョンのビルドシステムでは、このエラーを次のように報告していました。

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

最近のバージョンの Android ビルドシステムでは、異なるエラーが表示されますが、同じ問題を示しています。。

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

どちらのエラー状態でも 65,536 という数字が表示されます。これは重要な数字で、単一の Dalvik Executable(dex)バイトコード ファイル内でコードによって呼び出し可能な参照の総数を表しています。このページでは、Multidex というアプリ設定を有効にして、アプリで複数の DEX ファイルのビルドと読み取りを可能にすることで、上述の制限を回避する方法について説明します。

64K 参照制限について

Android アプリ(APK)ファイルには、Dalvik Executable(DEX)形式の実行可能なバイトコード ファイルが含まれており、その中にアプリの実行に使用されるコンパイル済みのコードがあります。Dalvik Executable の仕様により、単一の DEX ファイル内で参照できるメソッドの総数が 65,536 に制限されます。これらのメソッドには、Android フレームワーク メソッド、ライブラリ メソッド、独自のコードのメソッドが含まれます。コンピュータ サイエンスにおいては、Kilo、K は 1024(または、2^10)を表します。65,536 は 64 X 1024 に等しいため、この制限は「64K 参照制限」と呼ばれます。

Android 5.0 よりも前の Multidex サポート

Android 5.0(API レベル 21)よりも前のバージョンのプラットフォームでは、アプリコードを実行するために Dalvik ランタイムを使用します。デフォルトで、Dalvik はアプリに対して、APK ごとに 1 つの classes.dex バイトコード ファイルという制限を設けています。この制限を回避するために、アプリのプライマリ DEX ファイルの一部になっている Multidex Support Library を使用して、追加の DEX ファイルとこれらのファイルに含まれるコードへのアクセスを管理することができます。

注: minSdkVersion 20 以下を使用して Multidex 用にプロジェクトを設定し、Android 4.4(API レベル 20)以前を実行しているターゲット端末にデプロイしている場合、Android Studio では Instant Run を無効にします。

Android 5.0 以降の Multidex サポート

Android 5.0(API レベル 21)以降では、ART と呼ばれるランタイムが使用されており、APK ファイルからの複数 DEX ファイルの読み込みがネイティブでサポートされています。ART では、アプリのインストール時にプリコンパイルを実行し、classesN.dex ファイルをスキャンして、それらのファイルを Android 端末で実行可能な単一の .oat ファイルにコンパイルします。したがって、minSdkVersion が 21 以上である場合、Multidex サポート ライブラリは必要ありません。

Android 5.0 ランタイムの詳細については、ART と Dalvikをご覧ください。

注: Instant Run の使用時に、アプリの minSdkVersion が 21 以上に設定されている場合は、Android Studio によってアプリが自動的に Multidex 用に設定されます。Instant Run はアプリのデバッグ バージョンのみで機能するため、64K 制限を回避するためには、リリースビルドを Multidex 用に設定する必要があります。

64K 制限の回避

64K 以上のメソッド参照が有効になるようにアプリを設定する前に、アプリのコードや含まれているライブラリで定義されているメソッドなど、アプリのコードによって呼び出される参照の総数を削減する措置を講じる必要があります。以下は、DEX 参照制限に達することを回避するための戦略です。

これらのテクニックを使用することで、アプリで Multidex を有効化せずに済み、さらには APK 全体のサイズも削減されます。

Multidex を使用するためのアプリの設定

Multidex 設定を使用するようにアプリ プロジェクトを設定するには、アプリでサポートする最小 Android バージョンに応じて、アプリ プロジェクトに次の変更を加える必要があります。

minSdkVersion を 21 以上に設定している場合は、次に示すように、モジュール レベルの build.gradle ファイルで multiDexEnabledtrue に設定するだけです。

android {
    defaultConfig {
        ...
        minSdkVersion 21 
        targetSdkVersion 26
        multiDexEnabled true
    }
    ...
}

一方、minSdkVersion を 20 以下に設定している場合は、次のように Multidex サポート ライブラリを使用する必要があります。

これで、アプリのビルド時に、Android ビルドツールでプライマリ DEX ファイル(classes.dex)が作成され、必要に応じて補助的な DEX ファイル(classes2.dexclasses3.dex など)が作成されるようになります。次に、ビルドシステムは、すべての DEX ファイルを APK にパッケージ化します。

実行時に、Multidex API は特別なクラスローダを使用して、メインの classes.dex ファイルのみを検索するのではなく、メソッドに利用可能なすべての DEX ファイルを検索します。

Multidex Support Library の制限事項

Multidex Support Library には、注意すべき既知の制限事項がいくつかあり、アプリのビルド設定に Multidex Support Library を組み込む場合はそれらをテストする必要があります。

プライマリ DEX ファイルで必要なクラスの宣言

Multidex アプリ用に各 DEX ファイルを作成するときに、ビルドツールでは複雑な判断基準に基づいて、アプリを正常に起動するためにプライマリ DEX ファイルで必要となるクラスを決定します。起動時に必要ないずれかのクラスがプライマリ DEX ファイルで提供されていない場合、アプリはエラー java.lang.NoClassDefFoundError でクラッシュします。

アプリコードから直接アクセスされるコードの場合は、ビルドツールでそれらのコードパスが認識されるため、このエラーは発生しません。ただし、使用するライブラリに複雑な依存関係がある場合など、コードパスの可視性が低い場合には発生する可能性があります。たとえば、コードがイントロスペクションを使用している場合やネイティブ コードからの Java メソッド呼び出しを使用している場合、これらはプライマリ DEX ファイルで必要なクラスとして認識されない場合があります。

そのため、java.lang.NoClassDefFoundError を受信した場合には、これらの追加クラスを multiDexKeepFile またはビルドタイプの multiDexKeepProguard プロパティで宣言して、これらをプライマリ DEX ファイルで必要なクラスとして手動で指定する必要があります。クラスが multiDexKeepFile または multiDexKeepProguard ファイルで一致すると、そのクラスはプライマリ DEX ファイルに追加されます。

multiDexKeepFile プロパティ

multiDexKeepFile で指定するファイルには、com/example/MyClass.class の形式で 1 行ごとに 1 つのクラスが含まれている必要があります。たとえば、次のように multidex-config.txt というファイルを作成できます。

com/example/MyClass.class
com/example/MyOtherClass.class

次に、このファイルを以下のようにビルドタイプで宣言できます。

android {
    buildTypes {
        release {
            multiDexKeepFile file 'multidex-config.txt'
            ...
        }
    }
}

Gradle は build.gradle ファイルからの相対パスを読み取ることに注意してください。そのため、上記の例は、multidex-config.txtbuild.gradle ファイルと同じディレクトリにある場合のみ動作します。

multiDexKeepProguard プロパティ

multiDexKeepProguard ファイルは ProGuard と同じ形式を使用し、ProGuard の文法をすべてサポートします。ProGuard の形式と文法の詳細については、ProGuard マニュアルの keep オプションのセクションをご覧ください。

multiDexKeepProguard に指定するファイルには、正しい ProGuard 構文で -keep オプションが含まれている必要があります。たとえば、-keep com.example.MyClass.class のようになります。次のような multidex-config.pro というファイルを作成できます。

-keep class com.example.MyClass
-keep class com.example.MyClassToo

パッケージ内のすべてのクラスを指定する場合、ファイルは次のようになります。

-keep class com.example.** { *; } // All classes in the com.example package

次に、このファイルを以下のようにビルドタイプで宣言できます。

android {
    buildTypes {
        release {
            multiDexKeepProguard 'multidex-config.pro'
            ...
        }
    }
}

開発ビルドでの Multidex の最適化

Multidex 設定では、プライマリ DEX ファイル、セカンダリ DEX ファイルのそれぞれに含めるクラスを判別するためにビルドシステムで複雑な判断が必要になるため、ビルド処理に非常に長い時間がかかります。つまり、一般的に Multidex を使用した増分ビルドは時間がかかるため、開発プロセスの遅延につながる可能性があります。

Multidex のビルド時間を短縮するには、 productFlavors を使用して 2 つのビルド バリアントを作成します。1 つは開発フレーバー、もう 1 つはリリース フレーバーで、それぞれの minSdkVersion を異なる値にします。

開発フレーバーでは、minSdkVersion を 21 に設定します。この設定では、pre-dex というビルド機能が有効になります。この機能は、Android 5.0(API level 21)以上でのみ利用可能な ART 形式を使用して Multidex 出力をより速く生成します。リリース フレーバーでは、minSdkVersion を実際の最小サポートレベルに応じて設定します。この設定では、より多くの端末と互換性がある Multidex APK が生成されますが、ビルドに時間がかかります。

次のビルド設定のサンプルは、Gradle のビルドファイルでこれらのフレーバーをセットアップする方法を示しています。

android {
    defaultConfig {
        ...
        multiDexEnabled true
    }
    productFlavors {
        dev {
            // Enable pre-dexing to produce an APK that can be tested on
            // Android 5.0+ without the time-consuming DEX build processes.
            minSdkVersion 21
        }
        prod {
            // The actual minSdkVersion for the production version.
            minSdkVersion 14
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile 'com.android.support:multidex:1.0.1'
}

この設定変更の完了後に、増分ビルド用の devDebug バリアントをアプリに使用できます。このバリアントは dev プロダクト フレーバーの属性と debug ビルドタイプを組み合わせたものです。このバリアントにより、Multidex が有効化され、ProGuard が無効化されたデバッグ可能なアプリが作成されます(minifyEnabled はデフォルトで false であるため)。これらの設定により、Gradle 用の Android プラグインで以下が可能になります。

  1. pre-dex を実行する。各アプリ モジュールおよび各依存関係を独立した DEX ファイルとして作成します。
  2. 各 dex ファアイルを変更せずに(コードの圧縮を行わずに) APK に含める。
  3. モジュール DEX ファイルは統合されないため、プライマリ DEX ファイルのコンテンツを決定するための計算時間を短縮できる(最も重要)。

これらの設定では、変更されたモジュールの DEX ファイルのみが再計算されて、その後のビルド時に再パッケージ化されるため、高速の増分ビルドが実現します。ただし、これらのビルドで生成された APK は、Android 5.0 端末でのテストのみに使用できます。一方、この設定をフレーバーとして実装すると、リリースに適した最小 API レベルと ProGuard によるコード圧縮を使用して通常ビルドを実行する機能を保持することができます。

prodDebug バリアントのビルドなど、その他のバリアントをビルドすることも可能です。その場合、ビルド時間は長くなりますが、開発以外のテストに使用することができます。こうした設定の中では、prodRelease バリアントが最終テストおよびリリース用のバージョンになります。ビルド バリアントの使用の詳細については、ビルド バリアントの設定をご覧ください。

ヒント: これで、異なる Multidex のニーズに対応した異なるビルド バリアントを準備できました。また、各バリアントに異なるマニフェスト ファイルを提供することもできます(API レベル 20 以下用のバリアントのみ <application> タグ名が変更されます)。または、各バリアント用に異なる Application サブクラスを作成できます(API レベル 20 以下のバリアントのみ MultiDexApplication が拡張されるか MultiDex.install(this) が呼び出されます)。

Multidex アプリのテスト

Multidex アプリ用のインストルメンテーション テストを作成するときに、追加の設定は不要です。MultiDexApplication を使用しているか、カスタム Application オブジェクトで attachBaseContext() メソッドをオーバーライドし、MultiDex.install(this) を呼び出して Multidex を有効にしている限り、AndroidJUnitRunner はそのままの状態で Multidex をサポートします。

または、AndroidJUnitRunner で onCreate() メソッドをオーバーライドすることができます。

public void onCreate(Bundle arguments) {
    MultiDex.install(getTargetContext());
    super.onCreate(arguments);
    ...
}

注: テスト APK を作成する目的での Multidex の使用は、現在サポートされていません。

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)