ビブラートBoostMultiDex最適化実践:Androidアプリ初回起動時の低いバージョンが80%削減されました()

私たちが知っている、Androidの低いバージョン(4.X以下、SDK <21)機器は、Javaランタイム環境を使用すると、のDalvik仮想マシンです。これは、最大の問題は、更新プログラムのインストール後またはアップグレード最初の冷たい開始に長い時間がかかりということで、バージョンに比べて高いです。それは多くの場合、数十秒あるいは数分を要し、ユーザは、通常の使用APPを介して取得するには、この時間は空白の画面に直面しています。

これは非常に悪いユーザーエクスペリエンスです。我々はまた、オンラインデータ、アンドロイド4.Xから見つけることができ、モデルの下に、新規ユーザーにも一定の割合を占めますが、ユーザーの数に比べて少ない保持非常に追加する必要があります。特に海外では、そのような東南アジアや中南米など、ほかでローエンドのマシンにかなりの量を節約します。ユーザの次に低い4.Xバージョンは比較的小さいが、ビブラートし、そうではAPPの億ユーザTikTokサイズを有するが、さらには10%を占め、数も数千万人を持っています。あなたがシンクに市場を開放したいのであれば、ユーザの経験やアップグレードのこの部分は絶対に無視することはできませんされています。

問題の根本的な原因は、またはインストール後の最初の時間は、それは時間がかかりすぎるMultiDexをアップグレードすることです。この問題を解決するために、我々は、DEX-関連の処理ロジックのための基本となるシステムの仕組みのDalvik仮想マシンを掘る再設計、そして最終的にBoostMultiDexプログラムを立ち上げてきた、それはアップグレードAndroidユーザーの低いバージョン保存、黒待機時間の80%以上を削減することができますインストールエクスペリエンス。

私たちは、第1の比較データロードDEXのコールドスタート時間後、初めてのインストールを簡単に見てみましょう:

Androidのバージョン ファーム モデル オリジナルMultiDex消費する(S) BoostMultiDex消費する(S)
4.4.2 LG LGMS323 33.545 5.014
4.3.0 サムスン SGH-T999 30.331 3.791
4.2.1 HUAWEI G610-U00 36.465 4.981
4.1.2 サムスン I9100 30.962 5.345

あなたは、元のMultiDexプログラムが実際に完全なDEXの読み込みに半分以上の分を費やして、時間BoostMultiDexプログラムは、わずか5秒を必要と見ることができます。最適化の効果は非常に重要です!

次に、我々は詳細に開発プロセス全体BoostMultiDexプログラムおよびソリューションを説明する必要があります。

起因

問題の根本的な原因で見てみましょう。いくつかの理由の一般的な原因があります。

理解するあなた最初の必要性は、ここでは、Javaクラスにアクセスしたいということです必ずしもクラスローダを経由してアクセスにそれらをロードする必要があります。Androidでは、クラス内のAPPが作られていますPathClassLoaderロードを担当します。クラスが依存DEXファイルが使用するクラスのいずれかにするために、唯一の対応DEXがロードされ、存在しています。

AndroidのDEXの命令フォーマットのための初期のデザインは完璧ではない、単一DEXファイルで参照されるJavaメソッドの総数は65,536個を超えることはできません。

今APPのために、限りもう少し機能的な論理として、それがこの制限にタッチアップするのは簡単です。

メソッドAPP Javaコードの数が65,536を超えた場合、APPのコードが完全にDEXファイルに収まることはありませんしたがって、我々は、コンパイル時に、よりDEXファイルを生成する必要があります。私たちは、DEXファイルの数が含まれていないあなたが見ることができるAPK、のビブラートに巻か:

  8035972  00-00-1980 00:00   classes.dex
  8476188  00-00-1980 00:00   classes2.dex
  7882916  00-00-1980 00:00   classes3.dex
  9041240  00-00-1980 00:00   classes4.dex
  8646596  00-00-1980 00:00   classes5.dex
  8644640  00-00-1980 00:00   classes6.dex
  5888368  00-00-1980 00:00   classes7.dex
复制代码

アンドロイド4.4および使用以下のDalvik仮想マシン、通常の状況下では、のDalvik仮想マシンを実行することができる唯一のOPTは、私たちはしばしばODEXファイルを言って、あるDEXファイルを、最適化されませんでした。

APKのインストール、ときclasses.dexに自動的APPシステムによってODEXの最適化と起動デフォルトを行いますがに直接ロードPathClassLoaderして、内部classes.dexクラスは、私たちが心配することは確かに直接アクセスする必要はありませんすることができます。

そのあるDEXファイルに加えてclasses2.dexclasses3.dexclasses4.dexおよびその他のDEXファイル(ここでは、私たちをまとめてセカンダリDEXファイル)、これらのファイルは、私たち自身のODEXの最適化にする必要があり、およびクラス間正しく動作するために、クラスローダにロードされました。それ以外の場合はスローされますこれらのクラスにアクセスするときClassNotFoundにクラッシュを引き起こした例外を。

だから、Androidのは、正式にMultiDexプログラムを開始しました。APPがあるだけで、プログラムの最初の入り口で実行する必要があるApplication.attachBaseContextチューンの内側に直接MultiDex.install、それは目以降DEXファイル用のAPKパッケージのロックを解除しますODEXの最適化と負荷を行います。このように、複数のDEXファイルとのAPKが正常にもはや実行することができます。

この操作は、APPをインストールまたはアップデートした後、コールドスタートが行われる最初の時間になり、このプロセスが長く、時間のかかる黒い画面の問題を取るので、それが正確であることは私たちの最も冒頭で言及したの結果でした。

元の実装

このような背景を知って、私たちはMultiDex実装ロジックを見ていきますが比較的明確です。

まず、すべてのAPK classes2.dexclasses3.dexclasses4.dex他のDEXファイルとは、解凍します。

その後、各DEX行動ZIP圧縮のため。classesN.zipファイルを生成します。

その後、ODEXはclassesN.zip.odexファイルを生成するために、各ZIPファイルを最適化します。

具体的には、APPのcode_cacheカタログの下のファイルを見ることができます:

com.bytedance.app.boost_multidex-1.apk.classes2.dex
com.bytedance.app.boost_multidex-1.apk.classes2.zip
com.bytedance.app.boost_multidex-1.apk.classes3.dex
com.bytedance.app.boost_multidex-1.apk.classes3.zip
com.bytedance.app.boost_multidex-1.apk.classes4.dex
com.bytedance.app.boost_multidex-1.apk.classes4.zip
复制代码

これはによって行われるDexFile.loadDex実装される方法であって、専用ファイルの元とODEX ZIPファイルのパスを指定する必要があり、それがZIPに係るDEXの対応ODEX産物を生成することが可能であり、この方法は、最終的な返されDexFileたオブジェクトを。

最後に、APPこれらDexFileのオブジェクトが追加されている内部、あなたはにより、動作中のAPPをさせることができますロードDEXでこれらのクラスを使用します。PathClassLoaderpathListClassLoader

このプロセス全体では、生成しODEX ZIPファイルには、多くの二次DEXファイルAPPがある場合、それは問題を悪化させるだろう、比較的時間のかかるプロセスです。特にODEX生成処理、最大時間がかかるボトルネックの一つであるODEXファイルに変換されトラバース走査および最適化書換え処理にDEXファイル形式へのDalvik仮想マシン。

最適化は、一般的に使用されます

現在、業界では、我々は通常、このプロセスを最適化する方法で見てみましょうを最適化MultiDexいくつかの方法がありました。

の非同期読み込み

メインデックス内にパッケージ基づいて可能な限り使用するスタートアップ段階では、サービスコードセカンダリDEXを実行するために、できるだけ依存しません。その後、非同期呼び出しはMultiDex.install、とMultiDexが実行されていない場合は、必要に、セカンダリDEXを使用するには、その後の時点で、それはフォローアップのコードを続行する前に完了するのを待って同期を停止しました。

これは、コードの下の部分が完全にブロックされることはありませんしながら行っインストールしません。ただし、これを行うには、まずソート良いスタートロジックコード実行、並列に実行することができます正確に何を知っている必要があります。コード自体を置くことができ、主デックスが比較的限られているので、あまりにも依存がある場合、始動段階の動作は完全に、メインデックス内に挿入することができない、したがって、合理的に依存剥離要します。

そのため、現実的な状況下では、このプログラムの有効性は、より制限され、開始段階があまりにも多くのビジネスロジックを含む場合、多くの並列コードを実行することはできません、ブロックされたことをすぐにインストールします。

レイジーロード・モジュール

最初の米国のプログラムグループに登場この記事では、前回のプログラムのアップグレード版であると言うことができます。

これは、ロードされた非同期DEXを行うこともあるが、違いはそれは、私たちは、コンパイル時のDEXモジュールによって分割する必要があります。

活性は、最初のDEXに関連するプロバイダコードが中に置かれ、インターフェース、サービス、レシーバが一般的であり、そして2個、3ページの活動周波数インターフェースとDEXにおける二次への非コード。

完了していない場合は、まずこのモジュールのクラスがロードされているかどうか、モジュールを実装する必要が後ろに、そして待つ場合は、実行を継続する前に完了するために、インストールします。

目に見える、このパッケージのビジネスの変革の程度は非常に素晴らしいですが、すでにプロトタイプのプラグインフレームワークの数を持っています。さらに、我々は、クラスモジュールのケース決定をロード反射計装ActivityThreadを通じて注入を持って、ロジックアクティビティを実行する前に、自分の判断を挿入できるようにしたいです。これは、対応するモデルは互換性の問題を紹介します。

マルチスレッドロード

ネイティブMultiDex順序は、各DEXファイルODEXの最適化のために作られています。マルチスレッドの考え方は、すべてのDEX、各スレッドでそれぞれOPTを作ることです。

だから、一目見ただけで、ODEX最適化の結果を行う並列に再生することができるようです。しかし、我々は6つのセカンダリDEXファイルの合計が、道はほとんど効果を最適化するためにことがわかっ見つかり突出しています。その理由は、ODEX自体が同時にI / O操作で同時実行、複数のスレッドのため、実際の操作の深刻なI / O型であり、明らかな利点をもたらすことはできない、とマルチスレッドスイッチ自体はまた、いくつかの損失を持っているだろうことがあります。

バックグラウンドプロセスのロード

このプログラムは、メインプロセスはANRにあまりに長いリード用ODEXを行う防ぐために主にあります。あなたがAPPをクリックすると最初の個人が最初ODEXの非プライマリを行うためのプロセスを開始したときに、プライマリおよびその他の非仕上げ工程ODEXので、直接ODEXとで構成され、メインコースは直接実行することができない、メインコースの後に叫びました。しかし、これはメインプロセスANRの問題を回避するだけで、あなたが全体の待ち時間を最初に起動したときには減少しませんでした。

より徹底した最適化プログラム

すべてのレベルでいくつかのプログラム上記の最適化をしようとしているが、慎重な分析は、彼らが上にある根本的な問題は、触れていないことがわかりますMultiDex.install操作自体を。

MultiDex.installODEXファイル生成処理は、メソッドの呼び出しがあるDexFile.loadDex、それはdexopt DEXファイル入力ODEX変換の処理を開始します。だから、それを回避するかどうか、時間のこのODEX最適化?

私たちのBoostMultiDexプログラム、それが起動し、最適化に時間がかかり、自然からインストールするには、この時点からです。

我々のアプローチは、最初の起動時に、APPが正常に起動することができます行う前に、元DEXの直接OPT最適化をロードされていないされています。そして、バックグラウンドで別のプロセスを開始し、ゆっくりできる限りフォアグラウンドAPPの通常の使用に影響を与えることを避けるために、OPT DEX行われる作業。

違反

ここでの難しさは、もちろん - どのように行うには、直接、元のDEX、回避が障害物を持って来るために時間がかかるとODEXの最適化をロードすることができます。

あなたはODEXの最適化を避けたいが、また、通常のアプリケーションを実行できるようにしたい場合は、それが行わのDalvik仮想マシンのOPTを実行する必要がないことを意味し、本来のDEXは、ファイルを直接。仮想マシンは、DEXファイルを直接実行、それをサポートしていますか?直接生のDEXのバイトコードを実行することができ、すべてのDalvik仮想マシンの後、ODEX DEXはいくつかの追加の解析と最適化を行うために比較しました。だから、DEXが最適化されていない場合でも、理論的には、適切に実行することができるはずです。

ハードワークは、いくつかは、私たちの掘り、そして彼女はシステム内部で隠された入り口のDalvikのソースを発見した後、報わ:

/*
 * private static int openDexFile(byte[] fileContents) throws IOException
 *
 * Open a DEX file represented in a byte[], returning a pointer to our
 * internal data structure.
 *
 * The system will only perform "essential" optimizations on the given file.
 *
 */
static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args,
    JValue* pResult)
{
    ArrayObject* fileContentsObj = (ArrayObject*) args[0];
    u4 length;
    u1* pBytes;
    RawDexFile* pRawDexFile;
    DexOrJar* pDexOrJar = NULL;

    if (fileContentsObj == NULL) {
        dvmThrowNullPointerException("fileContents == null");
        RETURN_VOID();
    }

    /* TODO: Avoid making a copy of the array. (note array *is* modified) */
    length = fileContentsObj->length;
    pBytes = (u1*) malloc(length);

    if (pBytes == NULL) {
        dvmThrowRuntimeException("unable to allocate DEX memory");
        RETURN_VOID();
    }

    memcpy(pBytes, fileContentsObj->contents, length);

    if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
        ALOGV("Unable to open in-memory DEX file");
        free(pBytes);
        dvmThrowRuntimeException("unable to open in-memory DEX file");
        RETURN_VOID();
    }

    ALOGV("Opening in-memory DEX");
    pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
    pDexOrJar->isDex = true;
    pDexOrJar->pRawDexFile = pRawDexFile;
    pDexOrJar->pDexMemory = pBytes;
    pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
    addToDexFileTable(pDexOrJar);

    RETURN_PTR(pDexOrJar);
}
复制代码

この方法は、それが実際にいくつかの事ので行っている、ODEXファイルを頼らずに、元のDEXファイルの読み込みをするために行うことができます。

  1. それは受け入れbyte[]元DEXバイトコードファイルでパラメータを、。
  2. 呼び出しdvmRawDexFileOpenArrayプロセスに機能をbyte[]、生成するRawDexFileオブジェクトを
  3. RawDexFileオブジェクトの生成DexOrJar、によってaddToDexFileTableそれが従う通常使用することができるように、仮想マシンに追加します
  4. リターンDexOrJar上位層へのアドレスは、その上部の合法的な構築するためにクッキーとしてそれを使用することをDexFile目標に

このように、上部Seconary DEXは、すべての取得にDexFileオブジェクトをコールmakeDexElementsインストール動作を完了するためのClassLoaderの内部に挿入されます。このように、私たちはもうそれほど通常の実行APPことを、完全に最適化されたODEXを回避することができます。

探し入り口

我々が予期しない状態が発生したが、非常によく思われます。

私たちはDalvik_dalvik_system_DexFile_openDexFile_bytearray4.0〜4.3バージョンからJNIメソッドで、この関数の名前は、そのJavaのプロトタイプで見つけることができることが明らかに:

/*
 * Open a DEX file based on a {@code byte[]}. The value returned
 * is a magic VM cookie. On failure, a RuntimeException is thrown.
 */
native private static int openDexFile(byte[] fileContents);
复制代码

しかし、我々は4.4バージョンでは、Javaのネイティブメソッド層は、それが対応する必要はありません。だから我々は上に直接呼び出すことはできません。

もちろん、我々は簡単にあなたがコールに直接検索機能に署名するdlsymを使用することができ、と考えることができます。しかし残念ながら、Dalvik_dalvik_system_DexFile_openDexFile_bytearrayこの方法ではあるstaticので、それは輸出されていません。私たちは、実際に解析するために行ってきましたlibdvm.soが見つかりませんでしたまた、時間をDalvik_dalvik_system_DexFile_openDexFile_bytearrayこのシンボルを。

それがあるので、JNI関数は、通常の方法を介して仮想マシン内部に登録されています。したがって、我々は、レジストリに対応して機能を見つけることができます。

const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
    { "openDexFileNative",  "(Ljava/lang/String;Ljava/lang/String;I)I",
        Dalvik_dalvik_system_DexFile_openDexFileNative },
    { "openDexFile",        "([B)I",
        Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
    { "closeDexFile",       "(I)V",
        Dalvik_dalvik_system_DexFile_closeDexFile },
    { "defineClassNative",  "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
        Dalvik_dalvik_system_DexFile_defineClassNative },
    { "getClassNameList",   "(I)[Ljava/lang/String;",
        Dalvik_dalvik_system_DexFile_getClassNameList },
    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
        Dalvik_dalvik_system_DexFile_isDexOptNeeded },
    { NULL, NULL, NULL },
};
复制代码

dvm_dalvik_system_DexFileこの記号は、エクスポートすることは間違いないので、この配列には、実行時に登録された仮想マシンに動的にする必要があります。

このように、我々はまた、方法を検索するための1つの文字列マッチングによる要素の1つに従って、dlsymをすることで、この配列を得ることができopenDexFile、対応するDalvik_dalvik_system_DexFile_openDexFile_bytearray移動するための方法を。

以下を達成するために特定のコード:

    const char *name = "openDexFile";
    JNINativeMethod* func = (JNINativeMethod*) dlsym(handler, "dvm_dalvik_system_DexFile");;
    size_t len_name = strlen(name);
    while (func->name != nullptr) {
        if ((strncmp(name, func->name, len_name) == 0)
            && (strncmp("([B)I", func->signature, len_name) == 0)) {
            return reinterpret_cast<func_openDexFileBytes>(func->fnPtr);
        }
        func++;
    }
复制代码

ストローク明確な手順

ODEXは、主に以下の手順で、直接DEXプログラムをロードするもの、バイパスの概要:

  1. 二次DEXファイルはAPKから元のバイトコードを取得するために解凍しました
  2. dlsymを介して利用可能なdvm_dalvik_system_DexFile配列
  3. アレイでは、クエリを取得するDalvik_dalvik_system_DexFile_openDexFile_bytearray機能を
  4. この関数は、呼び出されるAPK DEXバイトコードから得たの着信前に一つずつ、完全なDEXがロードされ、合法的な取得されたDexFileターゲットを
  5. DexFileオブジェクトはのAPPに追加されPathClassLoaderpathlistにで

上記のいくつかの手順の完了は、私たちは二DEXのクラスの内部にアクセスすることができます

getDex問題

しかし、ちょうど我々が正常にダウンしますが、4.4のモデルにオリジナルのDEXの実行に注射すると、今すぐにクラッシュが発生します。

JNI WARNING: JNI function NewGlobalRef called with exception pending
             in Ljava/lang/Class;.getDex:()Lcom/android/dex/Dex; (NewGlobalRef)
Pending exception is:
java.lang.IndexOutOfBoundsException: index=0, limit=0
 at java.nio.Buffer.checkIndex(Buffer.java:156)
 at java.nio.DirectByteBuffer.get(DirectByteBuffer.java:157)
 at com.android.dex.Dex.create(Dex.java:129)
 at java.lang.Class.getDex(Native Method)
 at libcore.reflect.AnnotationAccess.getSignature(AnnotationAccess.java:447)
 at java.lang.Class.getGenericSuperclass(Class.java:824)
 at com.google.gson.reflect.TypeToken.getSuperclassTypeParameter(TypeToken.java:82)
 at com.google.gson.reflect.TypeToken.<init>(TypeToken.java:62)
 at com.google.gson.Gson$1.<init>(Gson.java:112)
 at com.google.gson.Gson.<clinit>(Gson.java:112)
... ...
复制代码

見ることができ、使用されにGsonのClass.getGenericSuperclass方法、およびそれが最後の呼び出しでClass.getDex、以下を達成するために対応し、ネイティブメソッドであります:

JNIEXPORT jobject JNICALL Java_java_lang_Class_getDex(JNIEnv* env, jclass javaClass) {
    Thread* self = dvmThreadSelf();
    ClassObject* c = (ClassObject*) dvmDecodeIndirectRef(self, javaClass);

    DvmDex* dvm_dex = c->pDvmDex;
    if (dvm_dex == NULL) {
        return NULL;
    }
    // Already cached?
    if (dvm_dex->dex_object != NULL) {
        return dvm_dex->dex_object;
    }
    jobject byte_buffer = env->NewDirectByteBuffer(dvm_dex->memMap.addr, dvm_dex->memMap.length);
    if (byte_buffer == NULL) {
        return NULL;
    }

    jclass com_android_dex_Dex = env->FindClass("com/android/dex/Dex");
    if (com_android_dex_Dex == NULL) {
        return NULL;
    }

    jmethodID com_android_dex_Dex_create =
            env->GetStaticMethodID(com_android_dex_Dex,
                                   "create", "(Ljava/nio/ByteBuffer;)Lcom/android/dex/Dex;");
    if (com_android_dex_Dex_create == NULL) {
        return NULL;
    }

    jvalue args[1];
    args[0].l = byte_buffer;
    jobject local_ref = env->CallStaticObjectMethodA(com_android_dex_Dex,
                                                     com_android_dex_Dex_create,
                                                     args);
    if (local_ref == NULL) {
        return NULL;
    }

    // Check another thread didn't cache an object, if we've won install the object.
    ScopedPthreadMutexLock lock(&dvm_dex->modLock);

    if (dvm_dex->dex_object == NULL) {
        dvm_dex->dex_object = env->NewGlobalRef(local_ref);
    }
    return dvm_dex->dex_object;
}

复制代码

崩壊のスタックとコード視点を結合するJNIの内部で実行されるcom.android.dex.Dex.create時間:

jobject local_ref = env->CallStaticObjectMethodA(com_android_dex_Dex,
                                                 com_android_dex_Dex_create,
                                                 args);
复制代码

それがJNIメソッドであるため、例外の後にこの呼び出しには、チェックがない場合にはフォローアップの実装で、発生env->NewGlobalRef前の例外へのコールチェックの時に発生し、これにスロー。

com.android.dex.Dex.createその理由は主に、パラメータに問題があるため、引数がされて、失敗したdvm_dex->memMapメモリのマップに運ば。dvm_dexは、このクラスの内部から作られています。仮想マシンコードの内部に、クラスに対応するそれぞれの構造でClassObjectのフィールド。

struct ClassObject : Object {
... ...
    /* DexFile from which we came; needed to resolve constant pool entries */
    /* (will be NULL for VM-generated, e.g. arrays and primitive classes) */
    DvmDex*         pDvmDex;
... ...
复制代码

ここでは、pDvmDexここでロードクラス割り当てのプロセス:

static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
    JValue* pResult)
{
... ...

    if (pDexOrJar->isDex)
        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    else
        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);

... ...
复制代码

pDvmDexdvmGetRawDexFileDexメソッド内で取得し、ここでパラメータpDexOrJar->pRawDexFile、それは私たちの目の前にあるopenDexFile_bytearray作成された内部はpDexOrJar、クッキーの前にトップに復帰することです。

その後によるとdvmGetRawDexFileDex

INLINE DvmDex* dvmGetRawDexFileDex(RawDexFile* pRawDexFile) {
    return pRawDexFile->pDvmDex;
}
复制代码

最終的にプッシュすることができ、dvm_dex->memMap対応しているopenDexFile_bytearray取得する時間pDexOrJar->pRawDexFile->pDvmDex->memMapときに我々はにないかどうか、DEXのバイト配列をロードしていたmemMapことが割り当て?

私たちは、コードを解析し、実際にケースが、ことがわかったmemMap。このフィールドは、ODEXの場合に割り当てられます。

/*
 * Given an open optimized DEX file, map it into read-only shared memory and
 * parse the contents.
 *
 * Returns nonzero on error.
 */
int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
... ...

    // 构造memMap
    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
        ALOGE("Unable to map file");
        goto bail;
    }

... ...

    // 赋值memMap
    /* tuck this into the DexFile so it gets released later */
    sysCopyMap(&pDvmDex->memMap, &memMap);

... ...
}
复制代码

ケース負荷のみDEXバイト配列とはmemMapに割り当てることができず、従って、このアプローチを取るとしないであろう。公式の最初からAndroidは、思わopenDexFile_bytearrayない良いサポート、我々は問題を公開するために、このメソッドを使用するように強制されるときに、どこにも使用していないシステム・コードではありません。

これは公式のピットですが、私たちが使用する必要があるため、あなたが埋めるために方法を考えなければなりません。

再分析のJava_java_lang_Class_getDexアプローチは、我々はこれを気づきました:

    if (dvm_dex->dex_object != NULL) {
        return dvm_dex->dex_object;
    }
复制代码

dvm_dex->dex_object空でない場合は、それが直接に戻ります、それは例外をスローしないように、memMap場所を取るために下に実行されません。我々完成負荷DEX配列はすぐに自分自身を生成した後、このように、思考を解決することは、非常に明確であるdex_objectターゲット、および注入されたpDvmDex内部を。

次のように詳細なコードは次のとおりです。

jclass clazz = env->FindClass("com/android/dex/Dex");
jobject dex_object = env->NewGlobalRef(
        env->NewObject(clazz),
        env->GetMethodID(clazz, "<init>", "([B)V"),
        bytes));
dexOrJar->pRawDexFile->pDvmDex->dex_object = dex_object;
复制代码

このような設定が行く後、getDex異常本当になくなります。

概要

この時点で、直接DEXローディング方式でのODEXの最適化を待たずに、完全に開放された、APPの最初の起動時間が大幅に低減することができます。

私たちの究極の極端な距離の短い距離のための完全なソリューションがある、しかし、それは、唯一の最も危険な厳しいこの短い方法です。大の課題は、待ち受けている、我々は慎重に、我々は次の記事を打破するだろうとして、また、最終的な収益をもたらすために詳細な計画を示すこと私たちは、最初に考慮されていないここで何の問題について考えることができます。

ビブラート/ TikTokのAndroidベースの技術チーム、上海、北京、深センでは、究極の深技術チームの追求で、杭州は人材ニーズをたくさん持っている、と私たちは、すべての学生がグローバル化APPの億人のユーザーを構築するために一緒に来て歓迎!

次のように入力し、元のテキストを読むためにクリックすることができますアンドロイド関連の仕事ビブラートの募集公式サイトを破っバイト、あなたにも接触することができます[email protected]内関連情報を参照するか、直接プッシュにあなたの履歴書を送って!

BoostMultiDex最適化の練習ビブラート、お楽しみに:APPは、Androidのバージョンが低い最初の開始時間に80%(b)は減少しています。

技術チームを破っようこそ懸念バイト

おすすめ

転載: juejin.im/post/5e5b9466518825494b3cd5aa