サイトのトップへ戻る

Android Studio ドキュメント 日本語訳

サイト内検索

メソッドが64000個を超えているアプリでの設定

Android プラットフォームは成長を続けているので、アプリのサイズも大きくなっています。 あなたのアプリとそれが参照するライブラリが特定のサイズまで達した時、 アプリがAndroid アプリのビルドアーキテクチャの限界に達したことを示すビルドエラーが発生します。 以前のバージョンのビルドシステムでは、このエラーは以下のように報告されます:

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

より新しいバージョンのビルドシステムでは異なるエラーが表示されます。それが示している問題の内容は同じです。:

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

これらのエラー状況には、両方とも共通する数字: 65,536が表示されています。 この数字は、一つの Dalvik Executable (DEX)バイトコードファイル内でコードによって呼び出すことができる参照の総数を表すので、重要なものです。 このページでは、multidexとして知られるアプリ設定を有効にすることでこの制限を回避する方法について説明します。 multidexを使うことで、アプリは複数のDEXファイルのビルドと読み込みができるようになります。



64K 参照制限について

Android app (APK) ファイルには、 Dalvik Executable (DEX) 形式の実行可能なバイトコードファイルが含まれています。 このファイルには、アプリの実行に使用されるコンパイル済みコードが含まれています。 Dalvik Executable の仕様では、一つのDEX ファイル内で参照できるメソッドの総数は65,536に制限されています。—これには Androidフレームワークメソッド、ライブラリメソッド、 自作コード内のメソッドが含まれます。 コンピューターサイエンスの文脈において、 Kilo, Kという用語は 1024 (もしくは 2^10)を表します。 65,536 は to 64 X 1024 の値と等しいので、この制限は '64K 参照制限'と呼ばれます。



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

Android 5.0 (API level 21)以前のバージョンのプラットフォームでは、アプリコードの実行に Dalvikランタイムを使用します。 既定では、Dalvik はアプリの classes.dex バイトコードファイルをAPKごとに一つと制限しています。 この制限を回避するため、multidex サポートライブラリを使います。 このライブラリはあなたのアプリのプライマリDEX ファイルの一部となり、追加DEXファイルとそれらが保持しているコードへのアクセスを管理します。

メモ: プロジェクトがminSdkVersion 20以下でmultidex の設定をしており、 Android 4.4 (API level 20)以下が動作している対象端末にデプロイする場合、 Android Studio は インスタントランを無効にします。



Android 5.0 以上でのMultidex サポート

Android 5.0 (API level 21) 以上では、ART と呼ばれるランタイムを使用します。これは、APKファイルからの複数のDEXファイルの読み込みをネイティブでサポートしています。 ARTはアプリインストール時に事前コンパイルを実行します。この時classesN.dex ファイルをスキャンし、それらを一つの .oat ファイルにコンパイルしてAndroid 端末で実行できるようにします。 従って、 minSdkVersionが 21 以上の場合はmultidex サポートライブラリは必要ありません。

Android 5.0ランタイムの詳細については ARTとDalvikを参照してください。

メモ:インスタントランを使用している間、 アプリのminSdkVersion が21以上の時にはAndroid Studio が自動的にアプリをmultidex 用に設定します。 Instant Runはアプリのデバッグバージョンでのみ動作するため、リリースビルドでは64K 制限を避けるためにmultidex 用の設定をする必要があります。



64K 制限を回避する

アプリで64K以上のメソッド参照を使用できるようにする前に、 アプリコードから呼び出される参照の総数を減らすようにしてください(アプリコードやインクルードしたライブラリで定義されているメソッドを含む)。 以下の方法論は、DEXの参照制限に遭遇するのを回避するのに役立ちます。:

  • アプリの直接的依存関係と推移的依存関係を見る - Ensure any large library dependency you include in your app is used in a manner that outweighs the amount of code being added to the app. よくあるアンチパターンは、少数の便利なユーティリティメソッドを使うために非常に大きなライブラリをインクルードすることです。 アプリの依存関係を減らすと、多くの場合でDEX参照制限を避けるのに役立つでしょう。
  • ProGuardを使って使用されていないコードを削除する - コード削減機能を有効にして、リリースビルドで ProGuardを実行します。コード削減機能を有効にすると、APKに未使用コードを含まないようにできます。

これらの技術を使うことで、multidexを有効にする必要性を回避し、APKの全体的なサイズを減らすこともできます。



アプリを multidex向けに設定する

アプリプロジェクトで multidex を使用できるよう設定するには、アプリプロジェクトで後述する変更を行う必要があります。 変更内容は、アプリがサポートする最小Androidバージョンによって異なります。

minSdkVersion が 21 以上の場合、必要なのは以下で示すように、モジュールレベルbuild.gradleファイルでmultiDexEnabled の設定値をtrueにすることだけです。:

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

しかし、 minSdkVersion が 20 以下の場合は、以下のようにして multidex サポートライブラリ を使用する必要があります:

  • 以下で示すように、モジュールレベル build.gradle ファイルを編集して multidex を有効にし、依存ファイルとして multidex ライブラリを追加します:

    android {
        defaultConfig {
            ...
            minSdkVersion 15 
            targetSdkVersion 25
            multiDexEnabled true
        }
        ...
    }
    
    dependencies {
      compile 'com.android.support:multidex:1.0.1'
    }
    
  • Applicationクラスを上書きしているかどうかに応じて、以下のうちどれか一つを実行します。:
    • Application クラスを上書きしていない場合、マニフェストファイルを編集して以下のように<application>タグ内に android:nameを設定します。 :

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.myapp">
          <application
                  android:name="android.support.multidex.MultiDexApplication" >
              ...
          </application>
      </manifest>
      
    • Applicationクラスを上書きしている場合、以下のようにして MultiDexApplicationを継承するように (可能であれば) 変更してください:

      public class MyApplication extends MultiDexApplication { ... }
      
    • もしくは、Applicationクラスを上書きしているが基底クラスの変更が不可能な場合は、代わりに attachBaseContext()メソッドを上書きして MultiDex.install(this) を呼び出し、 multidexを有効にすることができます:

      public class MyApplication extends SomeOtherApplication {
        @Override
        protected void attachBaseContext(Context base) {
           super.attachBaseContext(context);
           Multidex.install(this);
        }
      }
      

これで、アプリをビルドした時にAndroidビルドツールはプライマリDEXファイル(classes.dex)とサポートDEX ファイル (classes2.dex, classes3.dex, など) を必要に応じて作成します。 そしてビルドシステムは全ての DEX ファイルを APKにパッケージします。

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



multidex サポートライブラリの制限事項

multidexサポートライブラリには既知の制限事項がいくつかあります。 このライブラリをビルド構成に組み込み際にはこの制限事項に注意してテストを行う必要があります。:

  • 起動中にDEXファイルを端末のデータパーティションへインストールするのは複雑で、セカンダリDEXファイルが大きい場合はApplication Not Responding (ANR)エラーが発生することがあります。その場合、ProGuardを使ったコード削減 を適用してコードの未使用部分を削除する必要があります。
  • Dalvik linearAlloc バグ (Issue 22586)が原因で、multidex を使用しているアプリはAndroid 4.0 (API level 14)未満のバージョンのプラットフォームで動作している端末では起動できません。 API レベル14未満を対象としている場合は、起動時や特定のクラスグループを読み込んだ時にアプリに問題が発生する可能性があるので、 テストしているプラットフォームが該当バージョンでないか確認してください。 コード削減機能を使うと、これらの潜在的な問題を軽減、あるいは解決することができます。
  • multidexは非常に大きなメモリ割り当て要求をするため、Dalvik linearAlloc 制限 (Issue 78035)が原因で、 multidexを使用するアプリは実行時にクラッシュする可能性があります。 割り当て制限はAndroid 4.0 (API level 14)では増加しましたが、Android 5.0 (API level 21)より前のバージョンだとまだこの制限が適用される可能性があります。


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

multidex アプリ用の各DEX ファイルをビルドする際、 アプリが正常に起動できるようするため、ビルドツールはプライマリDEXファイルに必要なクラスはどれなのかを複雑な意思決定により決定します。 起動中に必要なクラスがプライマリDEX ファイルにない場合、アプリはjava.lang.NoClassDefFoundErrorエラーでクラッシュします。

この事象は、アプリのコード上から直接アクセスできるコードの場合は発生しません。 ビルドツールがこれらのコードパスを認識してくれるからです。 しかし、使用してるライブラリが複雑な依存関係を持っているといったようにコードパスが見えにくい場合は、この事象が発生する可能性があります。 例えば、ネイティブコード上のコードでJava メソッドの イントロスペクションや インボケーションを使用する場合、それらのクラスはプライマリDEX ファイルで必要なものと認識されないことがあります。

そのため java.lang.NoClassDefFoundErrorが発生した場合は、 ビルドタイプのmultiDexKeepFile プロパティに宣言を行うことで、それらの追加クラスをプライマリDEXファイルに必要なものとして手動で設定する必要があります。 ここで指定するファイルは、com/example/MyClass.classの形式にして、1行につき1クラスにする必要があります。 例えば、以下のような中身のdex.keepという名前のファイルを作成します:

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

それから以下のようにして、ビルドタイプでこのファイルを宣言します:

android {
    buildTypes {
        release {
            multiDexKeepFile file('dex.keep')
            ...
        }
    }
}

Gradle はbuild.gradleを基準とした相対パスを読み込むので、上記の例は dex.keepbuild.gradleと同じディレクトリにある場合に動作するということを覚えておいてください。



開発ビルドでのmultidex を最適化する

どのクラスをプライマリDEX ファイルにインクルードしてどのクラスをセカンダリDEX ファイルにインクルードするか、 ビルドシステムは複雑な判断を行う必要があるため、 multidex を使用するとビルド処理時間は大幅に増加します。 つまり、multidex を使ったインクリメンタルビルドは通常より長く時間がかかり、開発プロセスが遅延する可能性があるのです。

multidex 出力のビルド時間を短縮するには、 productFlavorsを使って二つのビルドバリアント(異なるminSdkVersion値を持った開発用フレーバーとリリース用フレーバー)を作成します。

開発用フレーバーには、 minSdkVersion に 21を設定します。 この設定で、pre-dexingと呼ばれるビルド機能が有効になります。 この機能では、 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バリアントを使用することができます。 devDebugとは、プロダクトフレーバーdev とビルドタイプdebugの各属性を組み合わせたものです。 これで、multidex が有効となってproguardが無効となっているデバッグ可能なアプリが作成されます (既定では minifyEnabled の設定値は false のため)。 これらの設定によって、Gradle 用Android プラグインは以下のように動作します:

  1. pre-dexingを実行します: 各アプリモジュールと各依存関係を個別のDEXファイルとしてビルドします。
  2. 変更を行わずに各 DEX ファイルを APK へ含めます (コード削減は行いません)。
  3. 最も重要なことですが、モジュールの DEX ファイルは結合されないので、プライマリDEX ファイルに入れる中身を決めるために長時間計算を行うといった事態が避けられます。

これらの設定の結果ビルドが高速化します。増分ビルドにより、ビルドの際には変更のあったモジュールのDEXファイルのみが再計算と再パッケージされるためです。 ですが、このビルドで作成されたAPKのテストで使うことができるのは Android 5.0の端末のみです。 しかし設定をフレーバーとして実装することで、リリースに適した最小APIレベルとProGuard コード削減処理を伴う通常のビルドを実行する機能を維持することができます。

また、これ以外のバリアント(prodDebugバリアントを含む)をビルドすることもできます。 prodDebugバリアントとは、開発環境外でもテストに使用することができるバリアントのことです。 示された設定の中では、prodReleaseバリアントが最終的なテストを行うリリース版のバリアントになります。 ビルドバリアントの使用に関する詳細についてはビルドバリアントを設定するを参照してください。

ヒント: これで、異なるmultidex ニーズごとの異なるビルドバリアントが準備できました。 さらに、各バリアントごとに異なるマニフェストファイルを用意したり(API level 20以下のバリアントでのみ <application> タグ名を変更するなど)、 各バリアントごとに異なる Applicationサブクラスを作成したりできます (API level 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 を使用することは、現時点ではサポートされていません。