Android 中的“64k”問題和分包

前段時間做項目,因爲項目應用了太多的第三方庫和包,也出現了著名了“64K”問題。就是說代碼中的一些東西超出了Android本身架構的極限。當時太着急,找到了解決的辦法就擱置了。今天在看Android官方文檔時發現,Android對這個問題早就做了處理。所以今天在此粘貼複製,做一個記錄。

隨着 Android 平臺的持續成長,Android 應用的大小也在增加。當我們的應用及其引用的庫達到特定大小時,就會遇到構建錯誤,指明應用已達到 Android 應用構建架構的極限。早期版本的構建系統按如下方式報告這一錯誤:

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

較新版本的 Android 構建系統雖然顯示的錯誤不同,但指示的是同一問題:

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

這些錯誤狀況都會顯示下面這個數字:65,536。這個數字很重要,因爲它代表的是單個 Dalvik Executable (dex) 字節碼文件內的代碼可調用的引用總數。如果構建的 Android 應用收到了這個錯誤,那麼恭喜你!本文介紹如何越過這一限制,繼續構建我們的應用。


關於 64K 引用限制:

Android 應用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可執行字節碼文件,其中包含用來運行我們的應用的已編譯代碼。Dalvik Executable 規範將可在單個 DEX 文件內可引用的方法總數限制在 65,536,其中包括 Android 框架方法、庫方法以及自己代碼中的方法。在計算機科學領域內,術語千(簡稱 K)表示 1024(或 2^10)。由於 65,536 等於 64 X 1024,因此這一限制也稱爲“64K 引用限制”。
越過這一限制需要您將應用構建流程配置爲生成多個 DEX 文件,這種配置稱爲 Dalvik 可執行文件分包配置。

Android 5.0 之前版本的 Dalvik 可執行文件分包支持

Android 5.0(API 級別 21)之前的平臺版本使用 Dalvik 運行時來執行應用代碼。默認情況下,Dalvik 限制應用的每個 APK 只能使用單個 classes.dex 字節碼文件。要想繞過這一限制,我們可以使用 Dalvik 可執行文件分包支持庫,它會成爲您的應用主要 DEX 文件的一部分,然後管理對其他 DEX 文件及其所包含代碼的訪問。

Android 5.0 及更高版本的 Dalvik 可執行文件分包支持
Android 5.0(API 級別 21)及更高版本使用名爲 ART 的運行時,後者原生支持從應用 APK 文件加載多個 dex 文件。ART 在應用安裝時執行預編譯,掃描 classes(..N).dex 文件,並將它們編譯成單個 .oat 文件,供 Android 設備執行。


規避 64K 限制

在將我們的應用配置爲支持使用 64K 或更多方法引用之前,我們應該採取措施減少應用代碼調用的引用總數,包括由我們的應用代碼或包含的庫定義的方法。下列策略可幫助避免達到 dex 引用限制:
檢查應用的直接和傳遞依賴項 - 確保在應用中使用任何龐大依賴庫所帶來的好處大於爲應用添加大量代碼所帶來的弊端。一種常見的反面模式是,僅僅爲了使用幾個實用方法就在應用中加入非常龐大的庫。減少您的應用代碼依賴項往往能夠幫助您規避 dex 引用限制。
通過 ProGuard 移除未使用的代碼 - 爲應用配置 ProGuard 設置以運行 ProGuard,並確保爲發佈構建啓用壓縮。啓用壓縮可確保您交付的 APK 不含有未使用的代碼。
運用這些技巧可幫助我們避免爲了在應用中支持更多的方法引用而需要進行的構建配置變更。這些措施還可以減小 APK 的大小,這對帶寬成本較高的市場特別重要。


通過 Gradle 配置應用進行 Dalvik 可執行文件分包

Android SDK Build Tools 21.1 及更高版本中提供的 Android Plugin for Gradle 支持以 Dalvik 可執行文件分包作爲構建配置的一部分。請務必使用 SDK 管理器將 Android SDK Build Tools 工具和 Android 支持存儲區更新至最新版本,然後再嘗試配置您的應用進行 Dalvik 可執行文件分包。
將應用開發項目設置爲使用 Dalvik 可執行文件分包配置需要對我們的應用開發項目做幾項修改。具體地講,您需要執行以下步驟:

      1.將 Gradle 構建配置更改爲啓用 Dalvik 可執行文件分包
      2.修改清單文件以引用 MultiDexApplication 類

修改模塊級 build.gradle 文件配置以加入支持庫和啓用 Dalvik 可執行文件分包輸出,如下面這段代碼中所示:

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"

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

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

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

在清單中,將 Dalvik 可執行文件分包支持庫中的 MultiDexApplication 類添加到 application 元素中

<?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>

將這些配置設置添加到應用後,Android 構建工具會根據需要構建主 dex (classes.dex) 和輔助 dex(classes2.dex、classes3.dex)。隨後構建系統會將它們打包成一個 APK 文件進行分發。

注:如果我們應用用途會擴展 Application 類,我們可以替換 attachBaseContext() 方法並調用 MultiDex.install(this) 來啓用 Dalvik 可執行文件分包。


Dalvik 可執行文件分包支持庫的侷限性:

Dalvik 可執行文件分包支持庫具有一些已知的侷限性,將其納入我們的應用構建配置之中時,應該注意這些侷限性並進行針對性的測試:
啓動期間向設備數據分區中安裝 .dex 文件的過程相當複雜,如果輔助 dex 文件較大,可能導致應用無響應 (ANR) 錯誤。在此情況下,我們應該通過 ProGuard 運用代碼壓縮技巧來儘量減小 dex 文件的大小,並移除未使用的那部分代碼。
由於存在 Dalvik linearAlloc 錯誤,使用 Dalvik 可執行文件分包的應用可能無法在運行的平臺版本低於 Android 4.0(API 級別 14)的設備上啓動。如果您的目標 API 級別小於 14,請務必針對這些版本的平臺進行測試,因爲應用可能會在啓動時或加載特定類羣時出現問題。代碼壓縮可以減少甚至有可能消除這些潛在問題。
由於存在 Dalvik linearAlloc 限制,如果使用 Dalvik 可執行文件分包配置的應用發出非常龐大的內存分配請求,可能會在運行時發生崩潰。儘管 Android 4.0(API 級別 14)提高了分配限制,但在 Android 5.0(API 級別 21)之前的 Android 版本上,應用仍有可能遭遇這一限制。
系統對於主 dex 文件在 Dalvik 運行時中執行時需要哪些類有着複雜的要求。Android 構建工具更新會處理這些 Android 要求,但所包括的其他庫也可能具有其他依賴項要求,包括使用自檢機制,或從原生代碼調用 Java 方法。一些庫可能要等到 Dalvik 可執行文件分包構建工具在更新後允許您對必須包括在主 dex 文件中的類進行指定時才能使用。


瞭解了原因,並完成以上過程操作後編譯項目,就不會出現64k的問題。若有相關更難解決的問題或者需要更詳細的可以看官方文檔:

https://developer.android.com/studio/build/multidex.html#mdex-gradle

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章