Android分包MultiDex原理詳解

MultiDex的產生背景   

當Android系統安裝一個應用的時候,有一步是對Dex進行優化,這個過程有一個專門的工具來處理,叫DexOpt。DexOpt的執行過程是在第一次加載Dex文件的時候執行的。這個過程會生成一個ODEX文件,即Optimised Dex。執行ODex的效率會比直接執行Dex文件的效率要高很多。

但是在早期的Android系統中,DexOpt有一個問題,DexOpt會把每一個類的方法id檢索起來,存在一個鏈表結構裏面。但是這個鏈表的長度是用一個short類型來保存的,導致了方法id的數目不能夠超過65536個。當一個項目足夠大的時候,顯然這個方法數的上限是不夠的。儘管在新版本的Android系統中,DexOpt修復了這個問題,但是我們仍然需要對低版本的Android系統做兼容。

爲了解決方法數超限的問題,需要將該dex文件拆成兩個或多個,爲此谷歌官方推出了multidex兼容包,配合AndroidStudio實現了一個APK包含多個dex的功能。


MultiDex的簡要原理

我們以APK中有兩個dex文件爲例,第二個dex文件爲classes2.dex。

  1. 兼容包在Applicaion實例化之後,會檢查系統版本是否支持 multidex,classes2.dex是否需要安裝。
  2. 如果需要安裝則會從APK中解壓出classes2.dex並將其拷貝到應用的沙盒目錄下。
  3. 通過反射將classes2.dex注入到當前的classloader中。

下面引入一下官方的文檔。

https://developer.android.com/tools/building/multidex.html#about 
筆者,針對官方文檔的翻譯如下:

構建超過65K方法的App

隨着Android設備的慢慢發展,App的大小會變得越來越大。當我們在開發App的時候由於報的大小和引用庫的原因,我們在編譯我們項目的時候通常會遇到下面一個錯誤:

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

當然,也有一些系統設備會出現以下log信息,不過反饋的是同一個問題:

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

這兩個錯誤條件顯示一個共同的數字:65536。這個數字,它表示的是你在一個dex包中的函數方法超過了65535個。

如果你已經構建了一個Android App時,並收到了這個錯誤,那麼恭喜你,你有很多代碼!

下面我們就具體說說,如何解決這個問題。


關於65K方法限制

我們知道Android中的可執行偉劍都存儲在dex文件中,其中包含已編譯的代碼來運行你的應用程序。Dalvik虛擬機對可執行dex文件的規格是有方法限制的,即一個單一的dex文件的方法總數最多爲65536。

其中包括:

  • 引用的Android Framework方法
  • library的方法
  • 我們自己書寫代碼的方法。

爲了突破這個方法數的限制,我們就提出了一個方案——生成多個dex文件。這個多個dex文件的方案,我們又稱爲multidex方案配置。

Multidex支持Android 5.0之前的版本 
    Android5.0版本的平臺之前,Android使用的是Dalvik Runtime執行的程序代碼。默認情況下,限制應用到一個單一的classes.dex。

Dalvik字節碼文件每APK。爲了繞過這個限制,你可以使用multidex支持庫,成爲你的應用程序的主要部分和DEX文件進行管理,獲得額外的dex文件,它們包含的代碼。

Multidex支持Android 5.0及更高版本 
    Android 5.0和更高的Runtime 如art,本身就支持從應用的APK文件加載多個DEX文件。art支持預編譯的應用程序在安裝時掃描類(..)。Dex文件編譯成一個單一的Android設備上執行.oat文件。


避免65K限制

當你確定使用multidex的分包策略的時候,請你先確定自己的代碼中都是優秀的。你還需要做以下幾步:

  • 去掉一些未使用的import和library
  • 使用ProGuard去掉一些未使用的代碼

用Gradle配置使用Multidex

Android 的 Gradle插件在 Android Build Tool 21.1開始就支持使用multidex了。

設置你的應用程序開發項目中使用multidex配置,要求你做出一些修改您的應用程序開發項目。:

  • 修改Gradle的配置,支持multidex
  • 修改你的manifest。讓其支持multidexapplication類

修改Gradle的build如下:

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"

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

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

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

Tips: 你可以在Gradle配置文件中的 multiDexEnabled 在 defaultConfig、 
buildType、productFlavor選項設置。

在manifest文件中,添加MultidexApplication Class的引用,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.multidex.myapplication">
    <application
        ...
        android:name="android.support.multidex.MultiDexApplication">
        ...
    </application>
</manifest>

當然,如果你重寫了 Application,就對自定義Application的繼承方式做一個修改。


Multidex的方式的侷限性

雖然我們開起來multidex是一個極好的東西,但是multidex還是存在自己的侷限性,我們在開發測試之前要清楚侷限性是什麼:

  1. 如果二DEX文件太大,安裝分割dex文件是一個複雜的過程,可能會導致應用程序無響應(ANR)的錯誤。在這種情況下,你應該儘量的減小dex文件的大小和刪除無用的邏輯,而不是完全依賴於multidex。
  2. 在Android 4.0設備(API Level 14)之前,由於Dalvik linearalloc bug(問題22586),multidex很可能是無法運行的。如果希望運行在Level 14之前的Android系統版本,請先確保完整的測試和使用。
  3. 應用程序使用了multiedex配置的,會造成使用比較大的內存。當然,可能還會引起dalvik虛擬機的崩潰(issue 78035)。
  4. 對於應用程序比較複雜的,存在較多的library的項目。multidex可能會造成不同依賴項目間的dex文件函數相互調用,找不到方法。

優化multidex開發和構建

一個multidex的配置,對系統apk的構建、簽名、打包複雜性大大的增加。這就意味着,你每一次的構建過程都是相當耗時的。

爲了加快我們的開發速度,加快構建的過程,我們可以在Gradle productFlavors新建出來一個 development flavor 和 production flavor 來滿足我們不同構建需求。

下面是一個列子演示我們如何設置這些flavors在Gradle build文件中:

android {
    productFlavors {
        // Define separate dev and prod product flavors.
        dev {
            // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
            // to pre-dex each module and produce an APK that can be tested on
            // Android Lollipop without time consuming dex merging processes.
            minSdkVersion 21
        }
        prod {
            // The actual minSdkVersion for the application.
            minSdkVersion 14
        }
    }
          ...
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile(proguard-android.txt),
                                                 proguard-rules.pro
        }
    }
}
dependencies {
  compile com.android.support:multidex:1.0.0
}

在你完成了傷處的配置修改之後,你配置productFlavor 和 buildType來使用 ,devDebug 變種app。使用這些變種app,可以設置proguard disable、multidex enable方便我們測試。

這些配置需要針對Android Gradle插件做如下操作:

  1. 在分包前,編譯應用程序中的每一個module包括依賴項目,這個步驟稱爲 pre-dexing。
  2. include每一個dex文件
  3. 最重要的是,對於主dex文件,不會做切分。以保證計算速度。

這樣設置既能夠保證我們的最終報是一個使用了multidex模式的,而又不影響我們平時開發的測試效率。


在Android Studio中使用變種App

使用multidex工具構建變種App是非常方便的。在Android Studio允許我們選擇這種變種構建方式的接口。

使用Android Studio構建 “devDebug”構建變種app需要完成兩步:

  • 打開變種編輯窗口,選擇favorites選項。
  • 點擊編譯不同的變種,如下圖所示 
    技術分享
發佈了86 篇原創文章 · 獲贊 6 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章