Android 突破64K方法數(shù)的限制

隨著安卓平臺的不斷發(fā)展與壯大,市場上大而全的應用比比皆是,產(chǎn)品需求的變更累積和UI交互的極致追求,除了 resources 文件的俱增,在 Android Project 中依賴的 Library 和 自己寫的 Java 代碼也會越來越多。這些變化,除了會導致打包出的 APK 文件越來越大之外,當項目中java代碼包含的方法數(shù)(method count)超出一個峰值時,編譯過程中就會出現(xiàn)如下錯誤:

較早版本的編譯系統(tǒng)中,錯誤內(nèi)容如下:

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

較新版本的編譯系統(tǒng)中,錯誤內(nèi)容如下:

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

盡管在不同版本的編譯系統(tǒng)中顯示的錯誤內(nèi)容不盡相同,但都提到了一個具體的數(shù)字:65536,也就是本文要講到的核心內(nèi)容,Android 64K Method Counts Limit 的峰值。詳細信息,參考官網(wǎng)用戶指南:Configure Apps with Over 64K Methods

Android 64K Method Counts Limit


Android Project 經(jīng)過編譯打包,其中的Java代碼(包括Library)轉(zhuǎn)化為DEX格式的字節(jié)碼文件,這是Android 5.0之前的Dalvik虛擬機決定的(5.0之后改為ART虛擬機),并且采用short類型引用DEX文件中的method,這也為method數(shù)量的峰值大小埋下了隱患。short類型能夠表示的最大值是65536,也就說單個DEX文件中最多只有65536個method能夠得到引用,如果代碼執(zhí)行了超出部分的method引用,自然會報錯,如methodNotFound等。1K等于1024,65536剛好是64K,為了便于稱呼和使用,就將這個限制統(tǒng)稱為64K方法數(shù)的引用限制。

為了解決64K方法數(shù)限制的問題,我們可以在項目中使用multidex配置,當項目中的方法數(shù)(包括:Android framework,library和我們自己寫的代碼)超過64K時,編譯系統(tǒng)會自動編譯出多個DEX文件。

Multidex Support


Android 5.0之前,安卓系統(tǒng)采用的是Dalvik虛擬機,采用的是JIT技術(Just-in-time compilation,即時編譯,運行時編譯DEX字節(jié)碼文件,這也是以前為什么安卓手機用戶總是詬病Android系統(tǒng)比iOS系統(tǒng)運行卡頓的原因),限制每個APK文件只能包含一個DEX文件(即classes.dex)。為了繞開這個限制,Google給我們提供了multidex support library兼容包,幫助我們實現(xiàn)應用程序加載多個DEX文件,并且這個兼容包作為程序的主DEX文件,管理者其他DEX文件的訪問。

注意:由于Instant Run機制利用的就是multidex原理,當項目中minSdkVersion參數(shù)設置為20或者更小,并且運行在Android 4.4 (API level 20)或更低版本的設備中時,Instant Run將失效。

Android 5.0之后,安卓系統(tǒng)改用了ART虛擬機(Android RunTime),采用的是OAT技術(Ahead-of-time,預編譯,在應用安裝的時候掃描應用中的所有DEX文件,并編譯成一個.oat格式的文件供安卓設備執(zhí)行,所以相比Dalvik虛擬機下的應用,安裝時間較長)。因此可以理解為,使用ART虛擬機下的安卓系統(tǒng)自動支持APK文件中多個DEX的加載。

注意:使用Instant Run時,如果項目中的minSdkVersion參數(shù)設為21或更高版本,Android Studio編譯運行時會自動使應用支持multidex。但Instant Run僅僅作用于debug版本,我們依然需要給release版本配置multidex來避開64K方法數(shù)的限制。

Config for Multidex With Gradle


Android Gradle 插件在 Android SDK Build Tools 21.1 及更高版本的編譯工具上支持multidex作為編譯配置的一部分,所以確保我們的Android SDK Build Tools tools已經(jīng)更新至21.1或更高版本,然后再來配置應用的multidex部分。

第一步,修改app/build.grale文件,使項目能夠使用multidex:

android {

    compileSdkVersion 21
    buildToolsVersion "21.1.0"

    defaultConfig {
        ...

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

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

第二步,修改AndroidManifest.xml文件,引用MultiDexApplication類:

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

添加這些配置后,編譯工具會構建出一個主DEX文件(classes.dex)和其他DEX文件(classes2.dex,classes3.dex等,如果需要的話),編譯系統(tǒng)會將他們打包到APK文件中。

注意:一般我們會在項目中自定義一個繼承自Application的類,此時就需要重寫attachBaseContext()方法,并在該方法里面調(diào)用MultiDex.install(this)來支持multidex,可參考:MultiDexApplication

Optimizing Multidex Development Builds


multidex配置下的應用,編譯系統(tǒng)需要經(jīng)過復雜的DEX分割運算,導致增加項目的編譯時間,從而影響開發(fā)人員的開發(fā)效率。我們可以使用productFlavors構建開發(fā)環(huán)境和正式環(huán)境的不同flavors來優(yōu)化multidex的長時間編譯問題。

對于development flavor,設置minSdkVersion值為21,運行在Android 5.0以上版本的設備中,使用ART-supported格式生成multidex的速度要快得多。對于release flavor,minSdkVersion值則設為應用實際支持的版本,編譯系統(tǒng)耗費較長的時間來生成適配多設備的multidex APK文件。如:

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'
}

這樣,在開發(fā)階段,使用devDebug類型的變種app,取消混淆,支持multidex,并且運行在5.0及以上版本的設備中,能夠加快編譯過程。有關flavors的信息,以前寫過一篇文章:Android 利用Gradle實現(xiàn)app的環(huán)境分離,更多信息可以參考英文手冊:Gradle Plugin User Guide,對應中文版譯文:Gradle Android插件用戶指南翻譯

Methods Count Statistics


盡管安卓系統(tǒng)支持multidex,我們還是要學會分析我們的應用,查看各個部分的方法數(shù),減少冗余方法。這里推薦幾個工具,幫助我們分析。

Library Methods count

一個在線統(tǒng)計 Android Library 方法數(shù)的網(wǎng)站,能夠統(tǒng)計出Android領域常見libraries的方法數(shù)、JAR文件和DEX文件大小,并且能夠選擇不同版本,以圖表的形式展示出來。

http://www.methodscount.com/

該網(wǎng)站也提供了Android Studio的插件,幫助我們分析項目中所依賴的libraries的方法數(shù),如圖所示:

methodscount-samples-02.png
methodscount-samples-03.png

Apk Method Count

一個在線統(tǒng)計 APK 文件方法數(shù)的開源項目,只需要將需要分析的APK文件拖拽上傳至此,即可得到分析結果,如圖:

apk-method-count-samples.png

Android Studio APK Analyzer

最后,要重磅推薦Android Studio自帶的APK Analyzer,功能齊全,使用方便,絕對是安卓開發(fā)人員分析應用的不二選擇。使用 Android Studio APK Analyzer ,我們至少能夠做到:

  • 查看APK壓縮文件中各個子文件的大小(如DEX和resource文件)

  • 理解DEX文件的結構

  • 快速查看APK文件的版本信息(直接查看AndroidManifest.xml內(nèi)容)

  • 直觀地比較兩個APK文件內(nèi)容

Android-Studio-APK-Analyzer-Samples.png

開發(fā)階段使用Android Studio打開一個項目時,有三種方式使用APK Analyzer工具:

  • 直接拖拽APK文件到Android Studio的編輯窗口

  • 雙擊打開項目目錄app/build/outputs/apk/下的APK文件

  • 點擊菜單欄Build->Analyse APK...并選擇APK文件

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容