Multi-dex とは
一個の dex に含められるメソッドの総数が 65,535 なのは有名な話ですが、Google Play Services や Guava などの大きなライブラリを使うと、わりとすぐにその数字に達してしまいます。
Guava の場合、ドメインに応じて dependency をつまみ食いできるので、不要なものは dependencies に記述しなければ良いだけで済みます。
しかし、Google Play Services は All in one なライブラリのため、つまみ食いができず、ProGuard で使わないものを無理やり削ぎ落とすなどの工夫が必要です。
それでも、ライブラリへの依存が増えれば、その分だけメソッドの総数も増えていきます。どうしても 65,535 を超える場合、Multi-dex Support を使うことで、この問題に対応することができます。
その名の通り、Multi-dex Support では、1 つの apk に dex ファイルを複数持たせることで、擬似的にアプリ全体としてのメソッドの総数を 65,535 以上持つことができるようにします。
Android Lollipop ではネイティブレベルでこのサポートが入るため、特別なことは何もありませんが、それより前の OS では、Dalvik は 1 つの dex ファイルしか読みに行かないため、パッチを当てる必要があります。Multi-dex Support はそのパッチを当てる役割を果たし、これによって、API Level 4 までを Multi-dex 対応することができるようになります。
準備
Multi-dex Support 専用の jar を導入します。
/sdk/extras/android/support/multidex/library/libs
に android-support-multidex.jar
があるので、これをコピペしてきます。
次に、Application クラスを魔改造します。
Application クラスがないのであれば、MultiDexApplication クラスを AndroidManifest の<application>
に指定します。
Application クラスがあって、親がApplication
であれば、MultiDexApplication
に変更するだけで済むでしょう。
何かしらの抽象Applicationクラスを継承しているのであれば、以下のように実装することで対応可能です。
public class MyApp extends HogeApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
最後に、ビルドスクリプトをいじって Multi-dex を使うようにします。
android {
dexOptions {
preDexLibraries = false
}
}
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += "--main-dex-list=$projectDir/multidex.keep".toString()
}
}
multidex.keep
によって、クラスローダが最初に読み込むclasses.dex
に含まれてほしいものを宣言します。ほぼ決まり文句のようなものです。これを記述しないと、運悪くclasses.dex
に以下の中間コードが含まれなかった場合にエラーとなります。
android/support/multidex/BuildConfig.class
android/support/multidex/MultiDex$V14.class
android/support/multidex/MultiDex$V19.class
android/support/multidex/MultiDex$V4.class
android/support/multidex/MultiDex.class
android/support/multidex/MultiDexApplication.class
android/support/multidex/MultiDexExtractor$1.class
android/support/multidex/MultiDexExtractor.class
android/support/multidex/ZipUtil$CentralDirectory.class
android/support/multidex/ZipUtil.class
この他、Application#onCreate
で直接いろいろなコンポーネントの初期化を行う場合、運悪く、対象のクラスがclasses.dex
に居ない時に、やはりクラスが見つからないエラーが出ます。回避策として、匿名の Runnable を用意し、Runnable#run()
で初期化処理を動かす方法があります。
ビルド
いつもどおりビルドするだけです。
いいこと
65,535 を超えるメソッドを取り扱うことができるようになるので、モノリシックなライブラリも怖くないですね。
よくないこと
当然ですが、アプリケーションの起動時に見るべきファイルが増えるので、その分だけ起動に時間がかかります。
たとえば、ブロードキャスト Intent が飛んできた時にアプリのプロセスが立ち上がると、その時に複数の dex からクラスをロードしようとするので、ブロードキャストの処理が終わるまでに時間がかかります。