1. ホーム
  2. Android

Android android-support-multidexを使用すると、Dexがメソッドの制限を超える問題を解決し、アプリケーションがバーストしなくなります。

2022-02-18 03:41:53

この記事を再掲載する場合は、出典を明記してください。Sands of Time:  http://blog.csdn.net/t12x3456     (サンドオブタイムのcsdnブログより)

アプリケーションの反復を続け、ビジネスラインが拡大するにつれ、アプリケーションはどんどん大きくなっていきます(例:様々なサードパーティのsdkや一般にサポートされているjarパッケージの統合、高いプロジェクト結合、繰り返しの多いクラス)、多くの方が以下のエラーに遭遇したことがあると思います。

UNEXPECTED TOP-LEVEL EXCEPTION:
IllegalArgumentException: method ID not in [0, 0xffff]: 65536
at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)
at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)
at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
at com.android.dx.command.dexer.Main.run(Main.java:230)
at com.android.dx.command.dexer.Main.main(Main.java:199)
main(Main.java:103) at com.android.dx.command.Main.main(Main.java:103)


はい、アプリ内のDexファイルメソッドの数が上限の65536を超えています。簡単に言うと、アプリが破裂している状態です。

        では、なぜこのエラーが発生するのかを見てみましょう。

        で アンドロイド システムで アプリ は、そのすべてのコードが デックス ファイルを作成します。 デックス はJarのようなファイルであり、複数の Java をコンパイルしたバイトコードをアーカイブファイルとして保存します。なぜなら アンドロイド システムは Dalvik を使用する仮想マシンを追加する必要があります。 Javaコンパイラ をコンパイルした後 クラス ファイルに変換されます。 Dalvik を実行ファイルに変換します。 クラス ファイルを作成します。ここで強調しておきたいのは デックス ジャー はアーカイブファイルであることに変わりはありません。 Java バイトコードファイルに対応するコードです。このとき アンドロイド システムは、アプリケーションを起動する際に、次のステップで デックス 最適化が行われますが、この処理を行うための専用ツールである デックスオプト . デックスオプト は、Dexファイルが最初にロードされたときに実行されます。この処理により ODEX ファイル、つまり 最適化デックス . を実行します。 ODex を直接実行するよりも効率的です。 デックス ファイルの方がはるかに効率的です。  しかし、以前の Android システムでは デックスオプト Android 2.2や2.3のLinearAllocはバッファが5MBでしたが、Android 4.xでは8MBや16MBに増えました。メソッド数がバッファサイズを超えると、dexoptはクラッシュしてインストールに失敗することがありました。 

        また、DEXファイル形式の制限により、DEXファイル内のメソッド数はネイティブ型のshortを使用してインデックスを作成するため、最大メソッド数は65536、フィールド/クラス数も制限されています。DEXファイルについては、プロジェクトで必要となるすべてのクラスファイルを1つのDEXファイルにまとめて圧縮しています。つまり、AndroidパッケージングのDEX処理(自社開発のコードと参照するAndroid Frameworkやサードパーティのライブラリ)では、1つのDEXファイルで参照できるメソッドの数は合計65536個に制限されているのです。

        現在よく使われているのは、(1)私が取り組んでいるプラグインフレームワークを使うなど、アプリケーションをプラグイン化する方法です。  https://github.com/singwhatiwanna/dynamic-load-apk それについて提案や質問がある場合は、気軽に ギズブ をGithubでご覧ください。(2) Dex,マルチプロジェクトの分割:必要な.classファイルまたはJarファイルをいくつかのソースコードと一緒にコンパイルしてJarファイルを生成します。その後、Android SDKが提供するdxツールを使って、JarファイルをDexファイルに変換します。(facebookを参照することができます。 https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920 2.3でのダイナミックな変化を見ることができます。 <スパン LinearAllocキャッシュ この2つのアプローチは衝突しません。また、アプリケーションのバーストを解決する以外にも、プラグインには多くの利点がありますが、それは前回の記事で確認できますので、繰り返しはしません。

        もちろんです。 グーグル は、現在のアプリケーションメソッドの爆発的な増加に問題意識を持っているようで、現在、アプリケーションの エーピーアイ21 で汎用的な解決策が提供されています。 android-support-multidex.jar . このjarパッケージは、最低でも以下のものまでサポートすることができます。 API 4 バージョン ( Android L をデフォルトでサポートします。 ミューティデックス ).

android-support-multidex.jarの適用方法について見てみましょう。 ( 以下はAnroid studioでの使用例で、eclipseで開発する場合はgradleプラグインをインストールする必要がありますが、他は基本的に同じです。 ):


最初の解決策は、-multi-dex設定(build.gradle)を使用して、classes.dex、classes2.dexなど複数のdexファイルを含むApkを生成することです。以下のように、build.gradleを修正する必要があります。
afterEvaluate {
    tasks.matching {
        it.name.startsWith('dex')
    }.each { dx ->
        if (dx.additionalParameters == null) {
            dx.additionalParameters = []
        }
        dx.additionalParameters += '--multi-dex' // enable multidex

        // optional
        // dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() // enable the main-dex-list
    }
}


しかし、デフォルトのDalvikクラスローダーはclasses.dexしか探さないので、それらを認識させるためにマージする必要があります。
もちろん、android.support.multidex.jarのサポートがあれば、すべてが非常に簡単になります。まずは該当するソースコードのディレクトリを見て、原理は後日説明します。
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



具体的な統合
プロジェクトに以下の設定を追加します。 build.gradle
android {
    defaultConfig {
        // Enabling multidex support.
        multiDexEnabled true
    true}
multiDexEnabled true }
dependencies { compile 'com.google.android:multidex:0.1'}



MultiDexの実装原理。

        Apk は実行時に dexpathlist を持ち、Multidex のソースコードは dexpathlist を修正し、システムのバージョン番号に基づいてすべての dex を dexpathlist に追加します。


次のインテグレーションは、2つのステップからなります。

1つ目 Import the android-support-multidex.jar from the sdkextrasandroid ✜multidexlibrary✜ directory into the project

II. プロジェクトにすでにアプリケーションクラスがある場合は、android.support.multidex.Applicationを継承させる。

     Application がすでに他のクラスを継承していて、それを変更したくない場合は、attachBaseContext() メソッドをオーバーライドして使用するという方法もあります。

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


最後にbuild.gradleのフルコンフィギュレーションが与えられます。

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"

    defaultConfig {
        ...
        minSdkVersion 14
        targetSdkVersion 21
        ...

        // Enabling multidex support.
        multiDexEnabled true
    multiDexEnabled true}
    ...
}

dependencies {
  compile 'com.android.support:multidex:1.0.0'
}



MutiDexの活用アイデア


I. MutiDexApplicationを継承した場合、またはアプリケーションでattachBaseContext()メソッドをオーバーライドした場合。

Applicationクラス内のロジックに関する注意事項 :

アプリケーション の静的なグローバル変数は、そのような MutiDex s インスタレーション() メソッドが最初にロードされるため、このメソッドをロードしないことが推奨されます。 アプリケーション クラスを参照するために静的変数を使用します。 メインクラス.dex ファイル外のdexファイル内のクラスは、以下のように変更することができます。

@Override
    public void onCreate() {
        super.onCreate();

        final Context mContext = this;
        new Runnable() {

            @Override
            public void run() {
                // put your logic here!
                // use the mContext instead of this here
            }
        }.run();
    }



II.  <スパン Googleはアプリ内のメソッドの総数制限の問題に対処しましたが、開発者が自由にプロジェクトを拡張できるわけではありません。multidexにはまだいくつかの制限があります。

  1. DEXファイルをデバイスにインストールする処理は複雑で、2つ目のDEXファイルが大きすぎるとアプリケーションが応答しなくなることがあります。この場合、ProGuardを使用してDEXファイルのサイズを小さくする必要があります。
  2. Dalvik linearAlloc のバグにより、Android 4.0 より前のバージョンでアプリが起動しないことがあります。
  3. また、Dalvik linearAllocの制限により、大量のメモリを要求するとクラッシュする可能性があります。アプリのインストール中、システムは dexopt というプログラムを実行して、現在の機種でアプリを実行する準備をします。dexopt は LinearAlloc を使用して、アプリのメソッドに関する情報を保存します。Android 2.2と2.3ではバッファは5MBでしたが、Android 4.xでは8MBまたは16MBに増えました。メソッドの数がバッファーのサイズを超えると、dexopt がクラッシュする原因になります。
  4. また、Multidex ビルドツールは、最初の DEX ファイルに含める必要があるクラスの指定に対応していないため、一部のライブラリ(ネイティブコードから Java コードへのアクセスが必要なクラスライブラリなど)が使用できなくなる可能性があります。

Mihai Parparita氏によるオープンソースプロジェクト、dex-method-countsは、APK内のパッケージごとのメソッド数をカウントすることができます。

開発者自身のコードでは、このようなメソッド数の制限に達することは難しい場合が多いですが、サードパーティのクラスライブラリが追加されると、メソッド数が急激に膨れ上がることがあります。そのため、Android 開発者にとっては、正しいクラスライブラリを選択することが特に重要です。

Google Guavaのような13,000以上のメソッドを含むクラスライブラリの使用は避けるべきでしょう。例えば、Jackson JSONをGoogle-gsonに置き換えたり、Google Protocol Buffersのようなデータ交換フォーマットでは、標準実装が自動的に大量のメソッドを生成してしまいます。Square Wireの実装は、この問題を解決する良い方法です。


<スパン よくある質問


DexException: ライブラリdexファイルはマルチdexモードではサポートされていません。 以下のようなエラーが表示されることがあります。


Error:Execution failed for task ':app:dexDebug'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
    $ANDROID_SDK/build-tools/android-4.4W/dx --dex --num-threads=4 --multi-dex
    ...
  Error Code:
    2
  Output:
    UNEXPECTED TOP-LEVEL EXCEPTION:
    DexException: Library dex files are not supported in multi-dex mode
        at com.android.dx.command.dexer.Main.runMultiDex(Main.java:322)
        at com.android.dx.command.dexer.Main.run(Main.java:228)
        at com.android.dx.command.dexer.Main.main(Main.java:199)
        main(Main.java:103) at com.android.dx.command.Main.main(Main.java:103)


dex の --multi-dex オプション設定はプリコンパイルされたライブラリプロジェクトと競合するため、アプリケーションに参照されるライブラリプロジェクトが含まれている場合、プリコンパイルを false に設定する必要があります。
android {
    // ...
    dexOptions {
        preDexLibraries = false
    }
}



OutOfMemoryError: Javaヒープスペース
実行時に以下のエラーが表示された場合。
UNEXPECTED TOP-LEVEL ERROR:
java.lang.OutOfMemoryError: Java heap space


dexOptions には、Java ヒープメモリのサイズを増加させるフィールドがあります。
android {
    // ...
    dexOptions {
        javaMaxHeapSize "2g"
    }
}    
        


eclipseを使用している開発者は、Googleの公式ドキュメントを参照することができます。

https://developer.android.com/tools/building/multidex.html#mdex-gradle

gradleのビルドをサポートするプラグインは、以下の場所でインストールとダウンロードが必要です。

http://dist.springsource.com/release/TOOLS/gradle   (この時、壁が必要な場合があります)



関連資料をご覧ください。

1. MutiDex公式ドキュメント : https://developer.android.com/reference/android/support/multidex/MultiDex.html

2. http://blog.osom.info/2014/10/multi-dex-to-rescue-from-infamous-65536.html

また、android -support-mutidex.jar も添付します。 <スパン ダウンロードのアドレスです。  http://download.csdn.net/detail/t12x3456/8143383


その他の注意事項

DexException: Multiple dex files define L{package}/BuildConfig;

このエラーが発生した場合は、以下をご確認ください。

1. メインプロジェクトが依存ライブラリプロジェクトのパッケージ名と重複しているかどうか
2. 2. メインプロジェクトと依存ライブラリプロジェクトにsupport.jarやその他のjarパッケージが重複して含まれていないか確認する。


解決策

1. ライブラリプロジェクトパッケージの名前を変更する

2. 重複するjarパッケージの削除

3. lib パッケージを手動で追加し、以下の構成を追加します。

dependencies {
            compile fileTree(dir: 'libs', include: ['*.jar'])
            compile project(':lib-project-module')
        }