アンドロイド:MultiDex原理と最適化

MultiDex:

Googleのサードパーティのライブラリ、android5.0 Googleはデックスは、実行時にロードされ、使用MultiDexライブラリのサポート、複数を提供して、以前に、より多くのdexのロードをサポートしていません。

バージョン5.0の下でも市場シェア率であり、技術ソリューションの原則プラグインMultiDexの内部原則と国内ホットフィックスの操作は、同じである場合。

クラスファイルとDexのファイル:

MultiDex =マルチ+デックス(多デックス)

DEX(のDalvik実行可能)

*の.java / .kt ----ソースコードコンパイラ、.classファイルをロードして実行することができる* JVMを生成します。

携帯電話のハードウェアはとても特別Androidプラットフォーム上で使用するために開発されたGoogleの、限定されているAndroid上で実行するためのプログラムのための仮想マシン環境を提供します。

ここでシステムの異なるバージョンの仮想マシンは、Androidプラットフォームは、に分けられます。

  • Dalvik VM
  • ART VM

2以上の異なるものであり、JVMの負荷をサポートしていないと、直接クラスファイルを実行ではなく、ソースコード内のクラスファイル、再建、解釈、または発生の圧縮工程の複数のクラスファイル、さらに翻訳にコンパイルされますDEXファイルをロードするには、Androidの仮想マシンは、実行を実行しています。

クラスの定数プール、フィールド情報、情報方法:クラスファイルは、クラスファイルに対応するすべての情報を記録します

DEXファイルにコンパイルされます収集されたすべてのクラスファイルは、DEXファイルは、クラスファイルの定数プールの前にすべての情報が含まれています。

冗長性除去のためのクラスファイルのDEXファイルなので、生成されたファイルの最終容量が小さく、速くています。 

問題と解決策のオーバーラン数: 

それは変更することができるようAPKは、基本的に圧縮されたパッケージであるの.apkに.zipサフィックス

抽出した後:

ネイティブコンパイルプロセスは、デフォルトのDEXファイルを生成します。

ときに、プロジェクトコードの量、多くの時間誤差まで:

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

それはしばしば言われる:65,536を超えるいくつかの方法を

DEXファイルには、クラスファイルの集まりです。

すなわち機会がインデックスに従う方法を、対応する仮想基準方法を実行する際にDEXファイルは、インデックスが割り当てられているすべてが複数のクラスの複数の方法を含むことができるです。前記方法は、数65536に制限されているので、インデックスは、0から65535の範囲です。

これらのメソッドは、次のとおりです。

  • メソッドの開発者が書かれています
  • サードパーティのライブラリー法

問題解決のアイデア:

  • 可能な限り、いくつかの方法がこの制限を超えません。
  • 不要なコードを削除し、混乱を削除しようとする必要があります。
  • 複数のDEXに分散MultiDex」=====(もっとDEXを生成する方法を、どのように余分なDEXがロードされ、実行されています)

MultiDexは、アプリケーション内の複数のDexの使用をサポートするGoogleのデックスファイルのサポートライブラリです。


MultiDexの使用:

  • Android5.0 +使い方
  • Android5.0-使い方
  • コンパイル済みのAPKパッケージ構造解析

1.Android5.0 +使用方法:

2.Android5.0-用法:

 

そして、いや、カスタムアプリケーションの時間:

MultiDexApplicationのカスタムアプリケーションを継承する必要があるとき。

元のコードは、ネイティブアプリケーションを継承していない場合は、中)(attachBaseContextに追加する必要があります

MultoDex.install(this)

前とAndroid5.0後、ここで示されたパケット構造生成:( multidexに使用した後に発生するAPKの変更)


MultiDex原理:

1.コンパイル原則:

classes.dexファイルを生成するためのAPKコンパイルプロセス、*。クラスファイルDXコマンドラインツール、

仮想マシンにクラスファイルの責任DXツールがDEXファイルを必要とします。

JARパッケージは、DEXファイルを生成できます。

dx --dex --output=<target.dex> origin.jar

--multi-DEXパラメータ: 

--multi-dex:
   allows to generate several dex files if needed.

 そのため、コンパイル時に--multi-DEXは、プロセスDX操作であり、制御パラメータは、DEX --multi-DEXを使用し、インストールパッケージと一緒に最終的なパッケージを取得するために、分割された複数のファイルを生成するAPKを実行することができます。

原則2.ファイル名を指定して実行:アン入口の分析と全体的なプロセス

AARは、インストールの論理演算を含んでいます。

エントリ・ポイントの分析:

  •  仮想マシンがMultiDexをサポートしているかどうかを確認
  • デックスの解凍は、インストールされるファイルのリストを取得します
  • デックスは、クラスローダに取り付けられました

3.仮想マシンの裁判官

 ARTとのDalvik仮想マシンの差:

Android4.4と次のバージョンは、JITののDalvik(タイムコンパイル)に対応java.vm.version <2.0.0、のDalvik仮想マシンを使用し

APK - >インストール - > * .DEX->スタート - > JIT->ネイティブコマンド - >ファイル名を指定して実行

どのJIT:その後、動的な高周波DEXバイトコードの実行時の実行とネイティブマシンコードの実行に変換、それは仕事を再実行するには再実行するたびニーズ、アプリケーションの動作中に発生しました。

  • スロースタート(キャッシュなし)
  • 遅い実行するために、より多くの電力

Android4.4後:AOT(事前コンパイラの)のART対応java.vm.version> = 2.0.0

APK - > INSTALL(AOT) - >ネイティブ命令 - > [スタート] - > [実行

前記AOT:アプリケーションをインストールする際には、ローカルマシン上で実行されているファイルのOATを生成し、ローカルに保存されていることができ、マシンコードにバイトコードをプリコンパイル、プリコンパイル、インストールパッケージファイルDEXのすべてのツールの使用が付属していますそれ以降は、コンパイルする必要はありません。

  • より速く起動します
  • ブロック操作、低消費電力化

したがって、上記の判断は、ソースコードがのDalvik仮想マシンまたはARTであるということです

4.Dex解凍:

List<? extends File> load(...){
  List files;
  if(!isModified(..)){//若apk未修改
     files = loadExistingExtractions(...);//加载之前解压的dex
  }else{
    files = performExtractions(...);//解压dex到指定的目录
    putStoredApkInfo(...);//保存已经解压的apk信息
  }
  return files;
}

解凍後、class2.dex APKファイルの元の存在が使用されるのを待って、組み込みのディレクトリデータ/データを使用するために抽出されます。

5.Dexインストール:

仮想マシンでは、結果の.classファイルをコンパイルし、実行にメモリにクラスローダの負荷によって必要とされています。アンドロイドアプリケーションが起動された後、システムは、私たちはメンバ変数のpathlistにしている負荷に作業クラスPathClassLoaderを作成するのに役立ちます:dexElements::要素[]、に対応し、配列の各要素DexPathListは、その内部要素の配列が含まれていますDEXファイルは、デフォルトのファイルシステムは、最初のDEX(class.dex)は、アレイケースをロードします。

あなたがクラスをロードする必要がある場合、実行時に、pathClassLoaderは、このようにクラスを完了し、ファイルが対応するクラスを持っているDEX見るためにすべての要素を前面から背面にpathlistにに直接的なリターンを配列要素を渡しますロード。

void install(){
  //反射获取到pathclassloader的dexpathlist
  Field pathListsField = Multidex.findField(loader,"pathList");
  Object dexPathList = pathListsField.get(loader);
//生成dex文件对应的element数组
   expandFieldArray(dexPathList,"dexElements",makeDexElements(...))
}

6.全体的なプロセス:

javacはクラスファイルを生成し、すべてのソースコードファイルをコンパイルして、複数のファイルは、DXツールをDEX生成します。

ランは、仮想マシンのバージョンを確認します。

仮想マシンが芸術である場合には、プロセスがマルチDEXファイル・システム・レベルをサポートしてきました実行はまた、オート麦ファイル、適用する不要になったときに、すべてのdexファイルは、アプリケーションのインストールファイルでオート麦、事前にマージされています自分の手続きを扱います。

Dalvik仮想マシンの場合、システムがマルチDEXファイルを処理のレベルをサポートしていない、あなたは自分の使用量dxをインストールする必要があり、特定のディレクトリアプリケーションにレベル2 DEXファイルを抽出する必要がある、レベル2のdexのリストを取得し、その後、レベル2 DEXリストは、クラスローダの操作に注入されます。


コードのホット修正:

  • コード説明ホット修正
  • コードのホット修正原理
  • コードのホット修正デモ

1.コードホット修正:

これはバグAPK時間をリリースしました:

オプション1:再リリースAPK

新APK-「棚アプリケーション市場 - 」 - 改訂版「コンパイラパッケージ」x.java-手動でダウンロードし、「アプリケーションを再起動する - 」apk-をインストールし、修復を完了するために、

  • 棚に戻ってリリース
  • ユーザーの知覚

シナリオ2:熱リハビリテーションプログラム

新しいパッチ - 「リモート髪 - 」バック静かにダウンロードしてパッチをインストール - 「アプリケーションを再起動する - 」修復を完了するために - x.java-「コンパイルパッチ」改定

  • 再パブリッシングアプリケーションなし
  • ユーザーは感じません

2.ホットコードの動作を修正します:

  • コードパッチを生成します

ToBeFixed.java-> javacの - 。>クラス - > DX-> patch.dex

  • ジェクトコードパッチパッケージの実行

PathClassLoader-> Pathlist-> dexElements

目的は、ホットフィックスパッチは修理の目的を達成するための優先ローディングシステムの範疇に含まれるようにすることです。

従ってpatch.dex dexElementsは、アレイ、すなわち、注入パッチの前部に挿入することができます。

3.コードの実装:

小さなデモでは、例えば、TextViewの設定は、ボタンをクリックしてのように修復することが表示されます

その後、パッチを生成します。

ソースコードに加えて、クラスを修復する削除します:

このファイルには、修復BUG iのソースコードに似ています。

対応するパッチを生成:

使用DEX DXコマンドは、ファイルを生成します。

現在使用されているプロジェクトのビルドツールのバージョンを表示します。

これは良いパッチを生成しました。

次のパッチは、実行時に注入されます。

package com.yinlei.multidexdemo;

import android.content.Context;
import android.os.Environment;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 运行时注入补丁包
 */
public class HotFixManager {
    public static final String FIXED_DEX_SDCARD_PATH = Environment.getExternalStorageDirectory().getPath() + "/fixed.dex";

    /**
     * 注入补丁包
     * @param context
     */
    public static void installFixedDex(Context context){
        try{
            //获取收集目录的补丁包
            File fixedDexFile = new File(FIXED_DEX_SDCARD_PATH);
            //文件不存在,说明不需要热修复
            if (!fixedDexFile.exists()){
                return;
            }
            // 获取PathCLassLoader的pathList字段
            Field pathListField = ReflectUtils.findField(context.getClassLoader(),"pathList");
            Object dexPathList = pathListField.get(context.getClassLoader());
            
            // 获取DexPathList中的makeDexElements方法
            Method makeDexElements = ReflectUtils.findMethod(dexPathList,"makeDexElements",
                    List.class,File.class,List.class,ClassLoader.class);
            // 把待加载的补丁文件添加到列表中
            ArrayList<File> filesToBeInstalled = new ArrayList<>();
            filesToBeInstalled.add(fixedDexFile);
            
            // 准备makeDexElements()的其他参数
            File optimizedDirecotry = new File(context.getFilesDir(),"fixed_dex");
            ArrayList<IOException> suppressedException = new ArrayList<>();
            
            //调用makeDexElements(),然后得到新的elements数组
            Object[] extraElements = (Object[]) makeDexElements.invoke(dexPathList,filesToBeInstalled,optimizedDirecotry,suppressedException,context.getClassLoader());
            
            //获取原始的elements数组
            Field dexElementsField = ReflectUtils.findField(dexPathList,"dexElements");
            Object[] originElements = (Object[]) dexElementsField.get(dexPathList);
            
            //数组的合并
            Object[] combinedElements = (Object[]) Array.newInstance(originElements.getClass().getComponentType(),originElements.length+extraElements.length);
            //在新的elements数组中先放入补丁包中的数组,再放原来的数组,以确保优先加载我们补丁包中的类
            System.arraycopy(extraElements,0,combinedElements, 0, extraElements.length);//深拷贝
            System.arraycopy(originElements,0,combinedElements,extraElements.length,originElements.length);
            
            // 用新的combinedElements,重新复制给dexPathList
            dexElementsField.set(dexPathList, combinedElements);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

最後のステップは、射出ロジックを開始することで、アクセス権が適用されます。

今見て修復されません。

まず、アプリケーションを強制終了、実行します。

 次に、アプリケーションを開いてプッシュします:

修理の後、今バージョンです。

ALL:

  • システムの異なるバージョンとAPIの互換性
  • 未実現リソースホットフィックスは、唯一のホットフィックスコードを実現するために、

MultiDexの最適化:

ANRを開始する予定1.MultiDex:

起動時に、Multidex次いでDEX解凍PathClassLoaderに注入し、その後、次のアプリケーション/データディレクトリに格納を抽出し、元のAPKからレベル2 DEXファイルを検索し、第1の噴射dexopt後の呼び出しがDEXファイルのために最適化されるであろう。クラスは.odexファイルによってロードされたときODEX、アプリケーションが実際にロードされました。

このプロセスの2つの可能な時間のかかる操作があります。

  • 解凍したファイル
  • プログラム実行dexopt

これらのプロセスは、典型的には、5SクリックイベントがANRの発生に応答して生じる超えないように、メインスレッドで実行されます。

2. MultiDexスタートの最適化:

問題のANRの原因は、メインプロセスのメインスレッドで実行時間のかかるIOプロセスです。アイデアは、メインプロセスを解決することではありませんので、時間のかかる操作は、初期立ちインストールされたアプリケーションで発生します。

attachBaseContextは()を実行するには時間がかかりMultiDex.installを()

代わりに、新しいプロセスでこれらの時間のかかる操作を行います。

新しいリモートブート場合は、元のマスター・プロセスはバックグラウンド・プロセスとなっている、それがハングアップのANRの問題は発生しません。

具体的なアイデア:

アプリケーションへのAPPのアプリケーションアイコンをクリックして - 「のメインプロセスを持ち上げる - 」Application.attachBaseContext-「デックスはすでに初期化されます。

初めてデックス動作にインストールしている場合は、multidex.install()とその後のアプリケーションの初期化プロセスを呼び出します。(DEXの解凍、インストール、初期化ファイルアプリケーション)

あなたが最初に初期化されませDEXをすれば、それはループに入ります。メインプロセスを中断し、かつ有無temp.fileを検出し続け、ちょうどループの外にあります。

同時にメインプロセスがハングした後、ロードプロセスのDEXが、引っ張っに)(アプリケーションを表示し、子スレッド、コールmultidex.installを作成するdexActivityスプラッシュスクリーンを解除された後、新しいプロセス(DEXロード処理)を開始インストールDEXを解凍した後、仕上げに、temp.fileついにdexActivityを作成します()。その後、DEXロード処理が終わって実行します。

中間ファイルtemp.fileデキサメタゾンロードプロセスが作成されたメインプロセス、見てそして、メインの処理ループ検出プロセスは、サイクルを停止し、全体のプロセスを完了するために、メインロジックの初期化処理を実施し続けています。

 

概要:キーポイントはつまりDEXインストール小さなデモの上には、このリンクに装備されています。

公開された363元の記事 ウォン称賛75 ビュー160 000 +

おすすめ

転載: blog.csdn.net/qq_39969226/article/details/105167392