隨著 Android 平臺(tái)的持續(xù)成長(zhǎng),Android 應(yīng)用的大小也在增加。當(dāng)應(yīng)用及其引用的內(nèi)容庫(kù)達(dá)到特定大小時(shí),會(huì)遇到構(gòu)建錯(cuò)誤,指明的應(yīng)用已達(dá)到 Android 應(yīng)用構(gòu)建架構(gòu)的極限。早期版本的構(gòu)建系統(tǒng)按如下方式報(bào)告這一錯(cuò)誤:
Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536較新版本的 Android 構(gòu)建系統(tǒng)雖然顯示的錯(cuò)誤不同,但指示的是同一問題:trouble writing output:Too many field references: 131000; max is 65536.You may try using --multi-dex option.這些錯(cuò)誤狀況都會(huì)顯示下面這個(gè)數(shù)字:65,536。這個(gè)數(shù)字很重要,因?yàn)樗淼氖菃蝹€(gè) Dalvik Executable (dex) 字節(jié)碼文件內(nèi)的代碼可調(diào)用的引用總數(shù)。本文介紹如何越過這一限制。
關(guān)于 64K 引用限制
Android 應(yīng)用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可執(zhí)行字節(jié)碼文件,其中包含用來(lái)運(yùn)行應(yīng)用的已編譯代碼。Dalvik Executable 規(guī)范將可在單個(gè) DEX 文件內(nèi)可引用的方法總數(shù)限制在 65,536,其中包括 Android 框架方法、內(nèi)容庫(kù)方法以及自己代碼中的方法。在計(jì)算機(jī)科學(xué)領(lǐng)域內(nèi),術(shù)語(yǔ)千(簡(jiǎn)稱 K)表示 1024(或 2^10)。由于 65,536 等于 64 X 1024,因此這一限制也稱為“64K 引用限制”。
越過這一限制需要將應(yīng)用構(gòu)建流程配置為生成多個(gè) DEX 文件,這種配置稱為 Dalvik 可執(zhí)行文件分包配置。
Android 5.0 之前版本的 Dalvik 可執(zhí)行文件分包支持
Android 5.0(API 級(jí)別 21)之前的平臺(tái)版本使用 Dalvik 運(yùn)行時(shí)來(lái)執(zhí)行應(yīng)用代碼。默認(rèn)情況下,Dalvik 限制應(yīng)用的每個(gè) APK 只能使用單個(gè) classes.dex 字節(jié)碼文件。要想繞過這一限制,可以使用 Dalvik 可執(zhí)行文件分包支持庫(kù),它會(huì)成為應(yīng)用主要 DEX 文件的一部分,然后管理對(duì)其他 DEX 文件及其所包含代碼的訪問。Android 5.0 及更高版本的 Dalvik 可執(zhí)行文件分包支持
Android 5.0(API 級(jí)別 21)及更高版本使用名為 ART 的運(yùn)行時(shí),后者原生支持從應(yīng)用 APK 文件加載多個(gè) dex 文件。ART 在應(yīng)用安裝時(shí)執(zhí)行預(yù)編譯,掃描 classes(..N).dex 文件,并將它們編譯成單個(gè) .oat 文件,供 Android 設(shè)備執(zhí)行。
規(guī)避 64K 限制
在將應(yīng)用配置為支持使用 64K 或更多方法引用之前,應(yīng)該采取措施減少應(yīng)用代碼調(diào)用的引用總數(shù),包括由應(yīng)用代碼或包含的內(nèi)容庫(kù)定義的方法。下列策略可幫助避免達(dá)到 dex 引用限制:
檢查應(yīng)用的直接和傳遞依賴項(xiàng) - 確保在應(yīng)用中使用任何龐大依賴庫(kù)所帶來(lái)的好處大于為應(yīng)用添加大量代碼所帶來(lái)的弊端。一種常見的反面模式是,僅僅為了使用幾個(gè)實(shí)用方法就在應(yīng)用中加入非常龐大的內(nèi)容庫(kù)。減少應(yīng)用代碼依賴項(xiàng)往往能夠幫助規(guī)避 dex 引用限制。通過 PRoGuard 移除未使用的代碼 - 為應(yīng)用配置 ProGuard 設(shè)置以運(yùn)行 ProGuard,并確保為發(fā)布構(gòu)建啟用壓縮。啟用壓縮可確保交付的 APK 不含有未使用的代碼。運(yùn)用這些技巧可幫助避免為了在應(yīng)用中支持更多的方法引用而需要進(jìn)行的構(gòu)建配置變更。這些措施還可以減小 APK 的大小,這對(duì)帶寬成本較高的市場(chǎng)特別重要
通過 Gradle 配置應(yīng)用進(jìn)行 Dalvik 可執(zhí)行文件分包
Android SDK Build Tools 21.1 及更高版本中提供的 Android Plugin for Gradle 支持以 Dalvik 可執(zhí)行文件分包作為構(gòu)建配置的一部分。務(wù)必使用 SDK 管理器將 Android SDK Build Tools 工具和 Android 支持存儲(chǔ)區(qū)更新至最新版本,然后再嘗試配置應(yīng)用進(jìn)行 Dalvik 可執(zhí)行文件分包。
將應(yīng)用開發(fā)項(xiàng)目設(shè)置為使用 Dalvik 可執(zhí)行文件分包配置需要對(duì)應(yīng)用開發(fā)項(xiàng)目做幾項(xiàng)修改。具體地講,需要執(zhí)行以下步驟:
將Gradle 構(gòu)建配置更改為啟用 Dalvik 可執(zhí)行文件分包修改清單以引用MultiDexapplication類修改模塊級(jí)
build.gradle文件配置以加入支持庫(kù)和啟用 Dalvik 可執(zhí)行文件分包輸出,如下面這段代碼中所示: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 可執(zhí)行文件分包支持庫(kù)中的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>將這些配置設(shè)置添加到應(yīng)用后,Android 構(gòu)建工具會(huì)根據(jù)需要構(gòu)建主 dex (classes.dex) 和輔助 dex(classes2.dex、classes3.dex)。隨后構(gòu)建系統(tǒng)會(huì)將它們打包成一個(gè) APK 文件進(jìn)行分發(fā)。Dalvik 可執(zhí)行文件分包支持庫(kù)的局限性
Dalvik 可執(zhí)行文件分包支持庫(kù)具有一些已知的局限性,將其納入應(yīng)用構(gòu)建配置之中時(shí),應(yīng)該注意這些局限性并進(jìn)行針對(duì)性的測(cè)試:
啟動(dòng)期間向設(shè)備數(shù)據(jù)分區(qū)中安裝 .dex 文件的過程相當(dāng)復(fù)雜,如果輔助 dex 文件較大,可能導(dǎo)致應(yīng)用無(wú)響應(yīng) (ANR) 錯(cuò)誤。在此情況下,應(yīng)該通過 ProGuard 運(yùn)用代碼壓縮技巧來(lái)盡量減小 dex 文件的大小,并移除未使用的那部分代碼。由于存在 Dalvik linearAlloc 錯(cuò)誤,使用 Dalvik 可執(zhí)行文件分包的應(yīng)用可能無(wú)法在運(yùn)行的平臺(tái)版本低于 Android 4.0(API 級(jí)別 14)的設(shè)備上啟動(dòng)。如果目標(biāo) API 級(jí)別小于 14,請(qǐng)務(wù)必針對(duì)這些版本的平臺(tái)進(jìn)行測(cè)試,因?yàn)閼?yīng)用可能會(huì)在啟動(dòng)時(shí)或加載特定類群時(shí)出現(xiàn)問題。代碼壓縮可以減少甚至有可能消除這些潛在問題。由于存在 Dalvik linearAlloc 限制,如果使用 Dalvik 可執(zhí)行文件分包配置的應(yīng)用發(fā)出非常龐大的內(nèi)存分配請(qǐng)求,可能會(huì)在運(yùn)行時(shí)發(fā)生崩潰。盡管 Android 4.0(API 級(jí)別 14)提高了分配限制,但在 Android 5.0(API 級(jí)別 21)之前的 Android 版本上,應(yīng)用仍有可能遭遇這一限制。系統(tǒng)對(duì)于主 dex 文件在 Dalvik 運(yùn)行時(shí)中執(zhí)行時(shí)需要哪些類有著復(fù)雜的要求。Android 構(gòu)建工具更新會(huì)處理這些 Android 要求,但所包括的其他內(nèi)容庫(kù)也可能具有其他依賴項(xiàng)要求,包括使用自檢機(jī)制,或從原生代碼調(diào)用 java 方法。一些內(nèi)容庫(kù)可能要等到 Dalvik 可執(zhí)行文件分包構(gòu)建工具在更新后允許對(duì)必須包括在主 dex 文件中的類進(jìn)行指定時(shí)才能使用優(yōu)化 Dalvik 可執(zhí)行文件分包開發(fā)構(gòu)建
Dalvik 可執(zhí)行文件分包配置大幅增加構(gòu)建處理時(shí)間,因?yàn)闃?gòu)建系統(tǒng)必須就哪些類必須包括在主 DEX 文件中以及哪些類可以包括在輔助 DEX 文件中作出復(fù)雜的決策。這意味著作為 Dalvik 可執(zhí)行文件分包開發(fā)流程的一部分執(zhí)行的例行構(gòu)建通常耗時(shí)更長(zhǎng),可能會(huì)拖慢開發(fā)進(jìn)度。
為了縮短通常耗時(shí)更長(zhǎng)的 Dalvik 可執(zhí)行文件分包輸出構(gòu)建時(shí)間,應(yīng)該利用 Android plugin for Gradle
productFlavors(一個(gè)開發(fā)定制和一個(gè)生產(chǎn)定制)在構(gòu)建輸出上創(chuàng)建兩個(gè)變型。對(duì)于開發(fā)定制,將最低 SDK 版本設(shè)置為 21。在此設(shè)置下,使用 ART 支持的格式時(shí)可大幅加快 Dalvik 可執(zhí)行文件分包的生成速度。對(duì)于發(fā)布定制,將最低 SDK 版本設(shè)置為與實(shí)際最低支持級(jí)別一致。此設(shè)置生成的 Dalvik 可執(zhí)行文件分包 APK 可兼容更多設(shè)備,但構(gòu)建時(shí)間更長(zhǎng)。
以下構(gòu)建配置示例展示了如何在 Gradle 構(gòu)建文件中設(shè)置這些定制:
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'}完成此配置變更后,可以使用應(yīng)用的
將應(yīng)用的每個(gè)模塊(包括依賴項(xiàng))構(gòu)建為單獨(dú)的 dex 文件。這通常稱為預(yù)先 dexing。將每個(gè) dex 文件加入 APK 時(shí)不做任何修改。最重要的是,模塊 dex 文件將不執(zhí)行合并操作,因此可以避免為確定主 dex 文件的內(nèi)容而進(jìn)行長(zhǎng)時(shí)間的計(jì)算。devDebug變體,后者集devproductFlavor 與debugbuildType 的屬性于一身。使用此目標(biāo)創(chuàng)建的將是停用 proguard、啟用 Dalvik 可執(zhí)行文件分包并將 minSdkVersion 設(shè)置為 Android API 級(jí)別 21 的調(diào)試應(yīng)用。這些設(shè)置會(huì)使 Android Gradle 插件執(zhí)行以下操作:這些設(shè)置的好處是,可以進(jìn)行快速的增量式構(gòu)建,因?yàn)橹挥行薷倪^的模塊的 dex 文件才需要重新計(jì)算并重新打包到 APK 文件中。這些構(gòu)建生成的 APK 只能用于在 Android 5.0 設(shè)備上進(jìn)行測(cè)試。不過,由于是以定制形式實(shí)現(xiàn)配置,保留了使用與發(fā)布相適的最低 SDK 級(jí)別和 proguard 設(shè)置執(zhí)行正常構(gòu)建的能力。
還可以構(gòu)建其他變體,包括
prodDebug變體,該變體雖然構(gòu)建時(shí)間更長(zhǎng),但可用于開發(fā)以外的測(cè)試。在所示配置內(nèi),prodRelease變體將是最終測(cè)試和發(fā)布版本。如果通過命令行執(zhí)行 Gradle 任務(wù),可以在標(biāo)準(zhǔn)命令末尾追加DevDebug(例如./gradlew installDevDebug)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注