6.11 配置構建 - 配置方法數超過 64K 的應用

隨著 Android 平臺的持續(xù)成長疏哗,Android 應用的大小也在增加。當您的應用及其引用的庫達到特定大小時禾怠,您會遇到構建錯誤返奉,指明您的應用已達到 Android 應用構建架構的極限。早期版本的構建系統(tǒng)按如下方式報告這一錯誤:

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

較新版本的 Android 構建系統(tǒng)雖然顯示的錯誤不同吗氏,但指示的是同一問題:

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

這些錯誤狀況都會顯示下面這個數字:65,536芽偏。這個數字很重要,因為它代表的是單個 Dalvik Executable (DEX) 字節(jié)碼文件內的代碼可調用的引用總數牲证。本頁介紹如何通過啟用被稱為 Dalvik 可執(zhí)行文件分包的應用配置來越過這一限制哮针,使您的應用能夠構建并讀取 Dalvik 可執(zhí)行文件分包 DEX 文件。

關于 64K 引用限制


Android 應用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可執(zhí)行字節(jié)碼文件坦袍,其中包含用來運行您的應用的已編譯代碼十厢。Dalvik Executable 規(guī)范將可在單個 DEX 文件內可引用的方法總數限制在 65,536,其中包括 Android 框架方法捂齐、庫方法以及您自己代碼中的方法蛮放。在計算機科學領域內,術語千(簡稱 K)表示 1024(或 2^10)奠宜。由于 65,536 等于 64 X 1024包颁,因此這一限制也稱為“64K 引用限制”。

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

Android 5.0(API 級別 21)之前的平臺版本使用 Dalvik 運行時來執(zhí)行應用代碼压真。默認情況下娩嚼,Dalvik 限制應用的每個 APK 只能使用單個 classes.dex 字節(jié)碼文件。要想繞過這一限制滴肿,您可以使用 Dalvik 可執(zhí)行文件分包支持庫岳悟,它會成為您的應用主要 DEX 文件的一部分,然后管理對其他 DEX 文件及其所包含代碼的訪問泼差。

:如果您的項目配置時所面向的 Dalvik 可執(zhí)行文件分包使用的是 minSdkVersion 20 或更低版本贵少,并且您將其部署到運行 Android 4.4(API 級別 20)或更低版本的目標設備上,則 Android Studio 會停用 Instant Run堆缘。

Android 5.0 及更高版本的 Dalvik 可執(zhí)行文件分包支持

Android 5.0(API 級別 21)及更高版本使用名為 ART 的運行時滔灶,后者原生支持從 APK 文件加載多個 DEX 文件。ART 在應用安裝時執(zhí)行預編譯吼肥,掃描 classes<var style="color: rgb(123, 31, 162); font-weight: 700;">N</var>.dex 文件录平,并將它們編譯成單個 .oat 文件麻车,供 Android 設備執(zhí)行。因此萄涯,如果您的 minSdkVersion 為 21 或更高值绪氛,則不需要 Dalvik 可執(zhí)行文件分包支持庫。

如需了解有關 Android 5.0 運行時的詳細信息涝影,請參閱 ART 和 Dalvik

:如果將應用的 minSdkVersion 設置為 21 或更高值争占,使用 Instant Run 時燃逻,Android Studio 會自動將應用配置為進行 Dalvik 可執(zhí)行文件分包。由于 Instant Run 僅適用于調試版本的應用臂痕,您仍需配置發(fā)布構建進行 Dalvik 可執(zhí)行文件分包伯襟,以規(guī)避 64K 限制。

規(guī)避 64K 限制


在將您的應用配置為支持使用 64K 或更多方法引用之前握童,您應該采取措施減少應用代碼調用的引用總數姆怪,包括由您的應用代碼或包含的庫定義的方法。下列策略可幫助您避免達到 DEX 引用限制:

  • 檢查您的應用的直接和傳遞依賴項 - 確保您在應用中使用任何龐大依賴庫所帶來的好處大于為應用添加大量代碼所帶來的弊端澡绩。一種常見的反面模式是稽揭,僅僅為了使用幾個實用方法就在應用中加入非常龐大的庫。減少您的應用代碼依賴項往往能夠幫助您規(guī)避 dex 引用限制肥卡。
  • 通過 ProGuard 移除未使用的代碼 - 為您的版本構建啟用代碼壓縮以運行 ProGuard溪掀。啟用壓縮可確保您交付的 APK 不含有未使用的代碼。

使用這些技巧使您不必在應用中啟用 Dalvik 可執(zhí)行文件分包步鉴,同時還會減小 APK 的總體大小揪胃。

配置您的應用進行 Dalvik 可執(zhí)行文件分包


將您的應用項目設置為使用 Dalvik 可執(zhí)行文件分包配置需要對您的應用項目進行以下修改,具體取決于應用支持的最低 Android 版本氛琢。

如果您的 minSdkVersion 設置為 21 或更高值喊递,您只需在模塊級 build.gradle 文件中將 multiDexEnabled 設置為 true,如此處所示:

android {
    defaultConfig {
        ...
        minSdkVersion 21 
        targetSdkVersion 26
        multiDexEnabled true
    }
    ...
}

但是阳似,如果您的 minSdkVersion 設置為 20 或更低值骚勘,則您必須按如下方式使用 Dalvik 可執(zhí)行文件分包支持庫

  • 修改模塊級 build.gradle 文件以啟用 Dalvik 可執(zhí)行文件分包,并將 Dalvik 可執(zhí)行文件分包庫添加為依賴項障般,如此處所示:
android {
    defaultConfig {
        ...
        minSdkVersion 15 
        targetSdkVersion 26
        multiDexEnabled true
    }
    ...
}

dependencies {
  compile 'com.android.support:multidex:1.0.1'
}
  • 根據是否要替換 Application類调鲸,執(zhí)行以下操作之一:

    • 如果您沒有替換 Application類,請編輯清單文件挽荡,按如下方式設置 <application> 標記中的 android:name
      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.myapp">
          <application
                  android:name="android.support.multidex.MultiDexApplication" >
              ...
          </application>
      </manifest>
      
    • 如果您替換了 Application類藐石,請按如下方式對其進行更改以擴 展 MultiDexApplication(如果可能):
      public class MyApplication extends MultiDexApplication { ... }
      
    • 或者,如果您替換了 Application類定拟,但無法更改基本類于微,則可以改為替換 attachBaseContext()方法并調用 MultiDex.install(this) 來啟用 Dalvik 可執(zhí)行文件分包:
      public class MyApplication extends SomeOtherApplication {
        @Override
        protected void attachBaseContext(Context base) {
           super.attachBaseContext(context);
           Multidex.install(this);
        }
      }
      

構建應用后逗嫡,Android 構建工具會根據需要構建主 DEX 文件 (classes.dex) 和輔助 DEX 文件(classes2.dex 和 classes3.dex 等)。然后株依,構建系統(tǒng)會將所有 DEX 文件打包到您的 APK 中驱证。

運行時,Dalvik 可執(zhí)行文件分包 API 使用特殊的類加載器來搜索適用于您的方法的所有 DEX 文件(而不是僅在主 classes.dex 文件中搜索)恋腕。

Dalvik 可執(zhí)行文件分包支持庫的局限性

Dalvik 可執(zhí)行文件分包支持庫具有一些已知的局限性抹锄,將其納入您的應用構建配置之中時,您應該注意這些局限性并進行針對性的測試:

  • 啟動期間在設備數據分區(qū)中安裝 DEX 文件的過程相當復雜荠藤,如果輔助 DEX 文件較大伙单,可能會導致應用無響應 (ANR) 錯誤。在此情況下哈肖,您應該通過 ProGuard 應用代碼壓縮以盡量減小 DEX 文件的大小吻育,并移除未使用的那部分代碼。
  • 由于存在 Dalvik linearAlloc 錯誤(問題 22586)淤井,使用 Dalvik 可執(zhí)行文件分包的應用可能無法在運行的平臺版本早于 Android 4.0(API 級別 14)的設備上啟動布疼。如果您的目標 API 級別低于 14,請務必針對這些版本的平臺進行測試币狠,因為您的應用可能會在啟動時或加載特定類群時出現問題游两。代碼壓縮可以減少甚至有可能消除這些潛在問題。
  • 由于存在 Dalvik linearAlloc 限制(問題 78035)总寻,因此器罐,如果使用 Dalvik 可執(zhí)行文件分包配置的應用發(fā)出非常龐大的內存分配請求,則可能會在運行期間發(fā)生崩潰渐行。盡管 Android 4.0(API 級別 14)提高了分配限制轰坊,但在 Android 5.0(API 級別 21)之前的 Android 版本上,應用仍有可能遭遇這一限制祟印。

聲明主 DEX 文件中需要的類


為 Dalvik 可執(zhí)行文件分包構建每個 DEX 文件時肴沫,構建工具會執(zhí)行復雜的決策制定來確定主要 DEX 文件中需要的類,以便應用能夠成功啟動蕴忆。如果啟動期間需要的任何類未在主 DEX 文件中提供颤芬,那么您的應用將崩潰并出現錯誤 java.lang.NoClassDefFoundError

該情況不應出現在直接從應用代碼訪問的代碼上套鹅,因為構建工具能識別這些代碼路徑站蝠,但可能在代碼路徑可見性較低(如使用的庫具有復雜的依賴項)時出現。例如卓鹿,如果代碼使用自檢機制或從原生代碼調用 Java 方法菱魔,那么這些類可能不會被識別為主 DEX 文件中的必需項。

因此吟孙,如果您收到 java.lang.NoClassDefFoundError澜倦,則必須使用構建類型中的 multiDexKeepFilemultiDexKeepProguard 屬性聲明它們聚蝶,以手動將這些其他類指定為主 DEX 文件中的必需項。如果類在 multiDexKeepFilemultiDexKeepProguard 文件中匹配藻治,則該類會添加至主 DEX 文件碘勉。

multiDexKeepFile 屬性

您在 multiDexKeepFile 中指定的文件應該每行包含一個類,并且采用 com/example/MyClass.class 的格式桩卵。例如验靡,您可以創(chuàng)建一個名為 multidex-config.txt 的文件,如下所示:

com/example/MyClass.class
com/example/MyOtherClass.class

然后吸占,您可以按以下方式針對構建類型聲明該文件:

android {
    buildTypes {
        release {
            multiDexKeepFile file 'multidex-config.txt'
            ...
        }
    }
}

請記住晴叨,Gradle 會讀取相對于 build.gradle 文件的路徑,因此如果 multidex-config.txt 與 build.gradle 文件在同一目錄中矾屯,以上示例將有效。

multiDexKeepProguard 屬性

multiDexKeepProguard 文件使用與 Proguard 相同的格式初厚,并且支持整個 Proguard 語法件蚕。如需了解有關 Proguard 格式和語法的詳細信息产禾,請參閱 Proguard 手冊中的 Keep Options 一節(jié)排作。

您在 multiDexKeepProguard 中指定的文件應該在任何有效的 ProGuard 語法中包含 -keep 選項。例如亚情,-keep com.example.MyClass.class妄痪。您可以創(chuàng)建一個名為 multidex-config.pro 的文件,如下所示:

-keep class com.example.MyClass
-keep class com.example.MyClassToo

如果您想要指定包中的所有類楞件,文件將如下所示:

-keep class com.example.** { *; } // All classes in the com.example package

然后衫生,您可以按以下方式針對構建類型聲明該文件:

android {
    buildTypes {
        release {
            multiDexKeepProguard 'multidex-config.pro'
            ...
        }
    }
}

優(yōu)化開發(fā)構建中的 Dalvik 可執(zhí)行文件分包


Dalvik 可執(zhí)行文件分包配置會大幅增加構建處理時間,因為構建系統(tǒng)必須就哪些類必須包括在主 DEX 文件中以及哪些類可以包括在輔助 DEX 文件中作出復雜的決策土浸。這意味著使用 Dalvik 可執(zhí)行文件分包的增量式構建通常耗時更長罪针,可能會拖慢您的開發(fā)進度。

為了縮短耗時更長的 Dalvik 可執(zhí)行文件分包輸出構建時間黄伊,請利用 productFlavors(一個開發(fā)定制和一個發(fā)布定制泪酱,具有不同的 minSdkVersion 值)創(chuàng)建兩個構建變型。

對于開發(fā)定制还最,將 minSdkVersion 設置為 21墓阀。該設置將啟用一個名為 pre-dexing 的構建功能,此功能使用僅適用于 Android 5.0(API 級別 21)和更高版本的 ART 格式更快生成 Dalvik 可執(zhí)行文件分包輸出拓轻。對于發(fā)布定制斯撮,將 minSdkVersion 設置為適于您的實際最低支持級別。此設置生成的 Dalvik 可執(zhí)行文件分包 APK 可兼容更多設備悦即,但構建時間更長吮成。

以下構建配置示例展示了如何在 Gradle 構建文件中設置這些定制:

android {
    defaultConfig {
        ...
        multiDexEnabled true
    }
    productFlavors {
        dev {
            // Enable pre-dexing to produce an APK that can be tested on
            // Android 5.0+ without the time-consuming DEX build processes.
            minSdkVersion 21
        }
        prod {
            // The actual minSdkVersion for the production version.
            minSdkVersion 14
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile 'com.android.support:multidex:1.0.1'
}

您完成此配置變更后橱乱,可以為增量式構建使用應用的 devDebug 變體,后者集 dev 產品定制與 debug 構建類型的屬性于一身粱甫。這將創(chuàng)建已啟用 Dalvik 可執(zhí)行文件分包且禁用 proguard 的可調試應用(因為 minifyEnabled 默認為 false)泳叠。這些設置會使適用于 Gradle 的 Android 插件執(zhí)行以下操作:

  1. 執(zhí)行 pre-dexing:將每個應用模塊和每個依賴項構建為單獨的 DEX 文件。
  2. 將每個 DEX 文件加入 APK茶宵,并且不做任何修改(不執(zhí)行代碼壓縮)危纫。
  3. 最重要的是,模塊 DEX 文件不執(zhí)行合并操作乌庶,因此可以避免為確定主 DEX 文件的內容而進行長時間的計算种蝶。

這些設置的好處是,可以進行快速的增量式構建瞒大,因為只有修改過的模塊的 DEX 文件才會在后續(xù)構建期間重新計算并重新打包螃征。但是,這些構建的 APK 只能用于在 Android 5.0 設備上進行測試透敌。不過盯滚,由于是以定制形式實現配置,您保留了使用與發(fā)布相適的最低 API 級別和 ProGuard 代碼壓縮執(zhí)行正常構建的能力酗电。

您還可以構建其他變體魄藕,包括 prodDebug 變體構建,該變體雖然構建時間更長撵术,但可用于開發(fā)以外的測試背率。在所示配置內,prodRelease 變體將是最終測試和發(fā)布版本嫩与。如需了解有關使用構建變體的詳細信息寝姿,請參閱配置構建變體

提示:由于您有適用于不同 Dalvik 可執(zhí)行文件分包需求的不同構建變體蕴纳,因此也可以為不同變體提供不同清單文件(這樣会油,只有適用于 API 級別 20 和更低版本的清單文件會更改 <application> 標記名稱),或者為每個變體創(chuàng)建不同的 Application 子類(這樣古毛,只有適用于 API 級別 20 和更低版本的清單文件會擴展 MultiDexApplication 類或調用 MultiDex.install(this))翻翩。

測試 Dalvik 可執(zhí)行文件分包應用


編寫面向 Dalvik 可執(zhí)行文件分包應用的儀器測試時,無需進行其他配置稻薇。AndroidJUnitRunner 直接支持 Dalvik 可執(zhí)行文件分包嫂冻,前提是您使用 MultiDexApplication 或替換您的自定義 Application 對象中的 attachBaseContext() 方法,并調用 MultiDex.install(this) 以啟用 Dalvik 可執(zhí)行文件分包塞椎。

或者桨仿,您可以替換 AndroidJUnitRunner 中的onCreate()方法:

public void onCreate(Bundle arguments) {
    MultiDex.install(getTargetContext());
    super.onCreate(arguments);
    ...
}

注:目前不支持使用 Dalvik 可執(zhí)行文件分包來創(chuàng)建測試 APK。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末案狠,一起剝皮案震驚了整個濱河市服傍,隨后出現的幾起案子钱雷,更是在濱河造成了極大的恐慌,老刑警劉巖吹零,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罩抗,死亡現場離奇詭異,居然都是意外死亡灿椅,警方通過查閱死者的電腦和手機套蒂,發(fā)現死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茫蛹,“玉大人操刀,你說我怎么就攤上這事∮ね荩” “怎么了骨坑?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長柬采。 經常有香客問我卡啰,道長,這世上最難降的妖魔是什么警没? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮振湾,結果婚禮上杀迹,老公的妹妹穿的比我還像新娘。我一直安慰自己押搪,他們只是感情好树酪,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著大州,像睡著了一般续语。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厦画,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天疮茄,我揣著相機與錄音,去河邊找鬼根暑。 笑死力试,一個胖子當著我的面吹牛,可吹牛的內容都是我干的排嫌。 我是一名探鬼主播畸裳,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淳地!你這毒婦竟也來了怖糊?” 一聲冷哼從身側響起帅容,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伍伤,沒想到半個月后并徘,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嚷缭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年饮亏,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阅爽。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡路幸,死狀恐怖,靈堂內的尸體忽然破棺而出付翁,到底是詐尸還是另有隱情简肴,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布百侧,位于F島的核電站砰识,受9級特大地震影響,放射性物質發(fā)生泄漏佣渴。R本人自食惡果不足惜辫狼,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辛润。 院中可真熱鬧膨处,春花似錦、人聲如沸砂竖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乎澄。三九已至突硝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間置济,已是汗流浹背音羞。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工矿瘦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滋饲,地道東北人燕酷。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像路媚,于是被迫代替她去往敵國和親黄琼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容