隨著 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) 字節碼文件內的代碼可調用的引用總數。本文介紹如何越過這一限制。
關于 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 可執行文件分包配置需要對應用開發項目做幾項修改。具體地講,需要執行以下步驟:
將Gradle 構建配置更改為啟用 Dalvik 可執行文件分包修改清單以引用MultiDexapplication
類修改模塊級
build.gradle
文件配置以加入支持庫和啟用 Dalvik 可執行文件分包輸出,如下面這段代碼中所示: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'}在manifest文件中,將 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 文件進行分發。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 文件中的類進行指定時才能使用優化 Dalvik 可執行文件分包開發構建
Dalvik 可執行文件分包配置大幅增加構建處理時間,因為構建系統必須就哪些類必須包括在主 DEX 文件中以及哪些類可以包括在輔助 DEX 文件中作出復雜的決策。這意味著作為 Dalvik 可執行文件分包開發流程的一部分執行的例行構建通常耗時更長,可能會拖慢開發進度。
為了縮短通常耗時更長的 Dalvik 可執行文件分包輸出構建時間,應該利用 Android plugin for Gradle
productFlavors
(一個開發定制和一個生產定制)在構建輸出上創建兩個變型。對于開發定制,將最低 SDK 版本設置為 21。在此設置下,使用 ART 支持的格式時可大幅加快 Dalvik 可執行文件分包的生成速度。對于發布定制,將最低 SDK 版本設置為與實際最低支持級別一致。此設置生成的 Dalvik 可執行文件分包 APK 可兼容更多設備,但構建時間更長。
以下構建配置示例展示了如何在 Gradle 構建文件中設置這些定制:
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'}完成此配置變更后,可以使用應用的
將應用的每個模塊(包括依賴項)構建為單獨的 dex 文件。這通常稱為預先 dexing。將每個 dex 文件加入 APK 時不做任何修改。最重要的是,模塊 dex 文件將不執行合并操作,因此可以避免為確定主 dex 文件的內容而進行長時間的計算。devDebug
變體,后者集dev
productFlavor 與debug
buildType 的屬性于一身。使用此目標創建的將是停用 proguard、啟用 Dalvik 可執行文件分包并將 minSdkVersion 設置為 Android API 級別 21 的調試應用。這些設置會使 Android Gradle 插件執行以下操作:這些設置的好處是,可以進行快速的增量式構建,因為只有修改過的模塊的 dex 文件才需要重新計算并重新打包到 APK 文件中。這些構建生成的 APK 只能用于在 Android 5.0 設備上進行測試。不過,由于是以定制形式實現配置,保留了使用與發布相適的最低 SDK 級別和 proguard 設置執行正常構建的能力。
還可以構建其他變體,包括
prodDebug
變體,該變體雖然構建時間更長,但可用于開發以外的測試。在所示配置內,prodRelease
變體將是最終測試和發布版本。如果通過命令行執行 Gradle 任務,可以在標準命令末尾追加DevDebug
(例如./gradlew installDevDebug
)。
新聞熱點
疑難解答