Android dex分包

Android中使用Dalvik虛擬機來運行應用程序,Dalvik虛擬機是有Google公司爲Android平臺量身定製的,它支持運行.dex格式的Java應用程序的運行。.dex文件將所有的.class文件打包,是專門Dalvik設計的一種壓縮格式。Android項目在打包成APK時,項目中所有的.class文件都已被打包在.dex文件中了。在Android系統安裝一個應用時,會對.dex文件進行優化,這個過程由專門的工具來處理,這個工具叫做DexOpt。DexOpt在第一次加載.dex文件時啓動,這個過程會產生一個ODEX文件,即Optimized Dex。經過預處理後的ODEX文件,在運行時會比.dex文件在速度上快很多。但是,在早期的Android系統中,DexOpt有一個bug,它在執行時會把每一個類的方法id檢索出來,存在一個鏈表結構中。這個鏈表的長度使用short類型保存的,這也就導致了方法的數目不能夠超過65536個。這裏的方法除了應用程序寫的方法,還有第三方庫中的方法,本地的方法。當一個項目的體積足夠龐大時,這個方法數目的上限是不能滿足需求的。在Andorid5.0之後,Android使用了ART(Android RunTime)來代替Dalvik虛擬機。ART本身就支持多dex的apk。它將多個dex文件組裝成oat文件再運行。

ART與Dalvik的區別。

儘管Android在新版本中解決了這個問題,但是爲了兼容低版本,我們還是要學習Android的dex分包。QQ空間的熱修復技術也是基於dex分包實現的。


下面介紹一下如何在gradle中實現dex分包
首先在module的build.gradle文件的dependencies模塊中添加如下語句,加載multidex包:

compile 'com.android.support:multidex:1.0.0'
然後在defaultConfig中添加註釋處的語句,設置mutiDex模式:
defaultConfig {
        applicationId "com.internetplus.yxy.intelligentspace"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        multiDexEnabled true //設置multiDex模式
    }

在AndroidManifest.xml文件中定義Application:
<application
        android:allowBackup="true"
        android:name=".MyApplication">
</application>

MyApplication類繼承MultiDexApplication,並在onCreate中註冊:

public class MyApplication extends MultiDexApplication {
public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
        FileDownloader.init(this);
        MultiDex.install(this);
}
}

或者在AndroidManifest.xml文件中直接使用 android.support.multidex.MultiDexApplication。還可以在MyApplication重寫attachBaseContext()方法,並使用上文中的方法進行註冊。

這樣,最基本的分包就已經完成了,當你的apk方法數大於65536的時候,apk就會自動分包。包的名稱爲classes.dex(此爲主dex包),classes2.dex,classes3.dex ...

如果你想做一些更詳細的控制,還可以添加相應的參數,比如指定哪些類放在主dex包中,每個包的方法數上限。在gradle1.5之前,可以在afterEvaluate語句中添加additionalParameters來進行控制:

afterEvaluate {
    tasks.matching {
        it.name.startsWith('dex')
    }.each { dx ->
        if (dx.additionalParameters == null) {
            dx.additionalParameters = []
        }
        dx.additionalParameters += '--multi-dex'
        dx.additionalParameters += '--set-max-idx-number=20000' // 設置每個dex包的最多方法數爲20000
    }
}
但在gradle1.5之後,修改了分包的參數api,這樣的設置方法已經無法使用了。但在2.2版本中,這些api又開放了,不過要在DexOptions中進行添加設置:
dexOptions {

        preDexLibraries = false
        additionalParameters = ['--multi-dex',
                                '--set-max-idx-number=20000']
    }
使用之前記得把jdk升到1.8再使用。


另附一個查看dex包中方法數的方法:

建立兩個文件:printhex.ps1 , dex-method-count.bat

printhex.ps1:

<#
.SYNOPSIS
Outputs the number of methods in a dex file.

.PARAMETER Path
Specifies the path to a file. Wildcards are not permitted.

#>
param(
  [parameter(Position=0,Mandatory=$TRUE)]
    [String] $Path
)

if ( -not (test-path -literalpath $Path) ) {
  write-error "Path '$Path' not found." -category ObjectNotFound
  exit
}

$item = get-item -literalpath $Path -force
if ( -not ($? -and ($item -is [System.IO.FileInfo])) ) {
  write-error "'$Path' is not a file in the file system." -category InvalidType
  exit
}

if ( $item.Length -gt [UInt32]::MaxValue ) {
  write-error "'$Path' is too large." -category OpenError
  exit
}

$stream = [System.IO.File]::OpenRead($item.FullName)
$buffer = new-object Byte[] 2
$stream.Position = 88
$bytesread = $stream.Read($buffer, 0, 2)
$output = $buffer[0..1] 
#("{1:X2} {0:X2}") -f $output
$outputdec = $buffer[1]*256 + $buffer[0]
"Number of methods is " + $outputdec
$stream.Close()

dex-method-count.bat:

@ECHO OFF

IF "%1"=="" GOTO MissingFileNameError

IF EXIST "%1" (GOTO ContinueProcessing) ELSE (GOTO FileDoesntExist)



:ContinueProcessing

set FileNameToProcess=%1

set FileNameForDx=%~n1.dex

IF "%~x1"==".dex" GOTO ProcessWithPowerShell



REM preprocess Jar with dx

IF "%~x1"==".jar" (

    ECHO Processing Jar %FileNameToProcess% with DX!

    CALL dx --dex --output=%FileNameForDx% %FileNameToProcess%

    set FileNameToProcess=%FileNameForDx%

    IF ERRORLEVEL 1 GOTO DxProcessingError

)



:ProcessWithPowerShell

ECHO Counting methods in DEX file %FileNameToProcess%

CALL powershell -noexit -executionpolicy bypass "& ".\printhex.ps1" %FileNameToProcess%

GOTO End



:MissingFileNameError

@ECHO Missing filename for processing

GOTO End



:DxProcessingError

@ECHO Error processing file %1% with dx!

GOTO End



:FileDoesntExist

@ECHO File %1% doesn't exist!

GOTO End



:End
然後將dex包和兩個文件放到一個目錄裏面,打開命令行並切換到當前目錄,運行:
dex-method-count.bat classes.dex
就可以看到dex包內的方法數了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章