如果你有這樣的問(wèn)題:
1.Dalvik和ART的區(qū)別
2.DEX在Dalvik轉(zhuǎn)化為ODEX和ART中轉(zhuǎn)化為ODEX的過(guò)程有上面區(qū)別
3.multidex在dalvik上起作用,ART上使用的也是multidex么(如果不是的話在application中寫(xiě)入multidex.install會(huì)對(duì)apk啟動(dòng)造成影響么)
如果你比較“懶”,,懶得看老羅的源碼分析,,,長(zhǎng)篇大論
請(qǐng)“簡(jiǎn)要”看完以下“簡(jiǎn)要”內(nèi)容
一:Dalvik和ART的區(qū)別#
Dalvik: Dalvik是Google公司自己設(shè)計(jì)用于Android平臺(tái)的Java虛擬機(jī)它可以支持已轉(zhuǎn)換為 .dex(即Dalvik Executable)格式的Java應(yīng)用程序的運(yùn)行,.dex格式是專(zhuān)為Dalvik設(shè)計(jì)的一種壓縮格式,適合內(nèi)存和處理器速度有限的系統(tǒng)。執(zhí)行的是字節(jié)碼,它是依靠Just-In-Time (JIT)機(jī)制去解釋字節(jié)碼
ART:即Android Runtime,google為了替代Dalvik專(zhuān)門(mén)為Android研發(fā)的。Android KK為開(kāi)發(fā)者推出,L版本正式上線。比替代品更高效省電,執(zhí)行的是本地機(jī)器碼(也就是linux的ELF文件格式),依靠Ahead-Of-Time (AOT)機(jī)制
二.在不同平臺(tái)DEX轉(zhuǎn)化為ODEX的過(guò)程#
簡(jiǎn)化流程如下:

這里參考的是
http://blog.csdn.net/luoshengyang/article/details/18006645
android安裝過(guò)程源碼分析:http://blog.csdn.net/luoshengyang/article/details/6747696
簡(jiǎn)單來(lái)說(shuō),就是Android系統(tǒng)通過(guò)PackageManagerService來(lái)安裝APK,在安裝的過(guò)程,PackageManagerService會(huì)通過(guò)另外一個(gè)類(lèi)Installer的成員函數(shù)dexopt來(lái)對(duì)APK里面的dex字節(jié)碼進(jìn)行優(yōu)化:
public final class Installer {
......
public int dexopt(String apkPath, int uid, boolean isPublic) {
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(uid);
builder.append(isPublic ? " 1" : " 0");
return execute(builder.toString());
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/pm/Installer.java中。Installer通過(guò)socket向守護(hù)進(jìn)程installd發(fā)送一個(gè)dexopt請(qǐng)求,這個(gè)請(qǐng)求是由installd里面的函數(shù)dexopt來(lái)處理的。詳細(xì)過(guò)程請(qǐng)移步Android ART運(yùn)行時(shí)無(wú)縫替換Dalvik虛擬機(jī)的過(guò)程分析
int dexopt(const char *apk_path, uid_t uid, int is_public)
{
struct utimbuf ut;
struct stat apk_stat, dex_stat;
char out_path[PKG_PATH_MAX];
char dexopt_flags[PROPERTY_VALUE_MAX];
char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX];
char *end;
int res, zip_fd=-1, out_fd=-1;
......
/* The command to run depend ones the value of persist.sys.dalvik.vm.lib */
property_get("persist.sys.dalvik.vm.lib", persist_sys_dalvik_vm_lib, "libdvm.so");
/* Before anything else: is there a .odex file? If so, we have
* precompiled the apk and there is nothing to do here.
*/
sprintf(out_path, "%s%s", apk_path, ".odex");
if (stat(out_path, &dex_stat) == 0) {
return 0;
}
if (create_cache_path(out_path, apk_path)) {
return -1;
}
......
out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644);
......
pid_t pid;
pid = fork();
if (pid == 0) {
......
if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
} else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
} else {
exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */
}
exit(68); /* only get here on exec failure */
}
......
}
……
static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,
const char* output_file_name, const char* dexopt_flags)
{
static const char* DEX_OPT_BIN = "/system/bin/dexopt";
static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
char zip_num[MAX_INT_LEN];
char odex_num[MAX_INT_LEN];
sprintf(zip_num, "%d", zip_fd);
sprintf(odex_num, "%d", odex_fd);
ALOGV("Running %s in=%s out=%s\n", DEX_OPT_BIN, input_file_name, output_file_name);
execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,
dexopt_flags, (char*) NULL);
ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));
}
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
const char* output_file_name, const char* dexopt_flags)
{
static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];
sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
execl(DEX2OAT_BIN, DEX2OAT_BIN,
zip_fd_arg, zip_location_arg,
oat_fd_arg, oat_location_arg,
(char*) NULL);
ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
}
函數(shù)定義在frameworks/native/cmds/installd/commands.c中 函數(shù)dexopt首先是讀取系統(tǒng)屬性persist.sys.dalvik.vm.lib的值,接著在/data/dalvik-cache目錄中創(chuàng)建一個(gè)odex文件。這個(gè)odex文件就是作為dex文件優(yōu)化后的輸出文件。再接下來(lái),函數(shù)dexopt通過(guò)fork來(lái)創(chuàng)建一個(gè)子進(jìn)程。如果系統(tǒng)屬性persist.sys.dalvik.vm.lib的值等于libdvm.so,那么該子進(jìn)程就會(huì)調(diào)用函數(shù)run_dexopt來(lái)將dex文件優(yōu)化成odex文件。另一方面,如果系統(tǒng)屬性persist.sys.dalvik.vm.lib的值等于libart.so,那么該子進(jìn)程就會(huì)調(diào)用函數(shù)run_dex2oat來(lái)將dex文件翻譯成oat文件,實(shí)際上就是將dex字節(jié)碼翻譯成本地機(jī)器碼,并且保存在一個(gè)oat文件中。
函數(shù)run_dexopt通過(guò)調(diào)用/system/bin/dexopt來(lái)對(duì)dex字節(jié)碼進(jìn)行優(yōu)化,而函數(shù)run_dex2oat通過(guò)調(diào)用/system/bin/dex2oat來(lái)將dex字節(jié)碼翻譯成本地機(jī)器碼。注意,無(wú)論是對(duì)dex字節(jié)碼進(jìn)行優(yōu)化,還是將dex字節(jié)碼翻譯成本地機(jī)器碼,最終得到的結(jié)果都是保存在相同名稱的一個(gè)odex文件里面的,但是前者對(duì)應(yīng)的是一個(gè)dey文件(表示這是一個(gè)優(yōu)化過(guò)的dex),后者對(duì)應(yīng)的是一個(gè)oat文件(實(shí)際上是一個(gè)自定義的elf文件,里面包含的都是本地機(jī)器指令)。通過(guò)這種方式,原來(lái)任何通過(guò)絕對(duì)路徑引用了該odex文件的代碼就都不需要修改了。
三.oat文件格式#
借助羅大神的圖我們可以知道,OAT文件本質(zhì)上是一個(gè)ELF文件,因此在最外層它具有一般ELF文件的結(jié)構(gòu),例如它有標(biāo)準(zhǔn)的ELF文件頭以及通過(guò)段(Section)來(lái)描述文件內(nèi)容。

OAT文件包含有兩個(gè)特殊的段oatdata和oatexec,前者包含有用來(lái)生成本地機(jī)器指令的dex文件內(nèi)容,后者包含有生成的本地機(jī)器指令,它們之間的關(guān)系通過(guò)儲(chǔ)存在oatdata段前面的oat頭部描述。
APK安裝過(guò)程中生成的OAT文件的輸入只有一個(gè)DEX文件,也就是來(lái)自于打包在要安裝的APK文件里面的classes.dex文件。實(shí)際上,一個(gè)OAT文件是可以由若干個(gè)DEX生成的。這意味著在生成的OAT文件的oatdata段中,包含有多個(gè)DEX文件。詳細(xì)分析請(qǐng)移步Android運(yùn)行時(shí)ART加載OAT文件的過(guò)程分析
四.multidex加載odex,multidex和oat的關(guān)系
MultiDex在dalvik虛擬機(jī)上的簡(jiǎn)要安裝過(guò)程:
將/data/app/apkName.apk路徑下解壓得到的classes2.dex, …, classesN.dex,依次寫(xiě)入到/data/data/pkgName/code_cache/secondary-dexes/apkName.apk.classes2.zip等zip文件的classes.dex中,并返回這個(gè)zip列表。然后針對(duì)這個(gè)zip列表執(zhí)行安裝過(guò)程,具體過(guò)程是,將這個(gè)要安裝的zip列表加入BaseDexClassLoader的pathList實(shí)例的dexElements數(shù)組中,其中會(huì)針對(duì)各dex文件進(jìn)行dex2opt優(yōu)化。一旦加入到了dexElements數(shù)組中,程序啟動(dòng)的時(shí)候,ClassLoader會(huì)加載dexElements數(shù)組中的元素,從而實(shí)現(xiàn)multi dex的安裝。
上面提到OAT文件可以由若干個(gè)dex生成,也就是不需要multidex去進(jìn)行安裝,但是multidex是application中
進(jìn)行Install的,跟虛擬機(jī)關(guān)系不大。
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
在install函數(shù)中在執(zhí)行從提取dex文件列表前會(huì)做一些校驗(yàn)操作,其中包含檢查APK是否已安裝,若APK已安裝,則不進(jìn)行后續(xù)操作。檢查SDK版本號(hào),版本號(hào)大于20不能保證MultiDex可正常Work
Set var2 = installedApk;
synchronized(installedApk) {
String apkPath = e.sourceDir;
if(installedApk.contains(apkPath)) {
return;
}
所以multidex在ART上不會(huì)影響程序的邏輯,它和ART沒(méi)有關(guān)系~。。。。
multidex源碼分析:MultiDex安裝過(guò)程源碼分析
小結(jié):
從安裝過(guò)程上來(lái)看
Java的代碼實(shí)際上需要兩次“轉(zhuǎn)換”才可以在android設(shè)備上運(yùn)行
一.PC端:.class->.dex->.apk
二.phone:dex->odex
區(qū)別在于第二步。
ART : .dex->.odex(機(jī)器碼)(AOT Ahead-Of-Time)
Dalvik: .dex->.odex(字節(jié)碼)(JIT Just-In-Time)
機(jī)器碼可直接執(zhí)行,而字節(jié)碼每次啟動(dòng)都需要執(zhí)行將優(yōu)化過(guò)的odex字節(jié)碼再轉(zhuǎn)換成機(jī)器碼
ART優(yōu)缺####
系統(tǒng)性能大幅提升
App啟動(dòng)、運(yùn)行更快
減少每次啟動(dòng)的編譯增加電池續(xù)航
存儲(chǔ)占用更大
安裝時(shí)間更長(zhǎng)
復(fù)制粘貼+理解,,,希望以后忘了能回來(lái)看看~!~
關(guān)注微信公眾號(hào) Android歷練記 或掃一掃二維碼:
讓我們一起來(lái)搞事情。
