Dalvik,ART與ODEX相愛相生

如果你有這樣的問題:
1.Dalvik和ART的區(qū)別
2.DEX在Dalvik轉(zhuǎn)化為ODEX和ART中轉(zhuǎn)化為ODEX的過程有上面區(qū)別
3.multidex在dalvik上起作用,ART上使用的也是multidex么(如果不是的話在application中寫入multidex.install會對apk啟動造成影響么)

如果你比較“懶”,,懶得看老羅的源碼分析,,,長篇大論
請“簡要”看完以下“簡要”內(nèi)容

一:Dalvik和ART的區(qū)別#

Dalvik: Dalvik是Google公司自己設(shè)計用于Android平臺的Java虛擬機(jī)它可以支持已轉(zhuǎn)換為 .dex(即Dalvik Executable)格式的Java應(yīng)用程序的運(yùn)行,.dex格式是專為Dalvik設(shè)計的一種壓縮格式,適合內(nèi)存和處理器速度有限的系統(tǒng)。執(zhí)行的是字節(jié)碼,它是依靠Just-In-Time (JIT)機(jī)制去解釋字節(jié)碼
ART:即Android Runtime,google為了替代Dalvik專門為Android研發(fā)的。Android KK為開發(fā)者推出,L版本正式上線。比替代品更高效省電,執(zhí)行的是本地機(jī)器碼(也就是linux的ELF文件格式),依靠Ahead-Of-Time (AOT)機(jī)制

二.在不同平臺DEX轉(zhuǎn)化為ODEX的過程#

簡化流程如下:


打包安裝運(yùn)行簡化流程.png

這里參考的是
http://blog.csdn.net/luoshengyang/article/details/18006645

android安裝過程源碼分析:http://blog.csdn.net/luoshengyang/article/details/6747696
簡單來說,就是Android系統(tǒng)通過PackageManagerService來安裝APK,在安裝的過程,PackageManagerService會通過另外一個類Installer的成員函數(shù)dexopt來對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());  
    }  
  
    ......  
}  

這個函數(shù)定義在文件frameworks/base/services/java/com/android/server/pm/Installer.java中。Installer通過socket向守護(hù)進(jìn)程installd發(fā)送一個dexopt請求,這個請求是由installd里面的函數(shù)dexopt來處理的。詳細(xì)過程請移步Android ART運(yùn)行時無縫替換Dalvik虛擬機(jī)的過程分析

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)建一個odex文件。這個odex文件就是作為dex文件優(yōu)化后的輸出文件。再接下來,函數(shù)dexopt通過fork來創(chuàng)建一個子進(jìn)程。如果系統(tǒng)屬性persist.sys.dalvik.vm.lib的值等于libdvm.so,那么該子進(jìn)程就會調(diào)用函數(shù)run_dexopt來將dex文件優(yōu)化成odex文件。另一方面,如果系統(tǒng)屬性persist.sys.dalvik.vm.lib的值等于libart.so,那么該子進(jìn)程就會調(diào)用函數(shù)run_dex2oat來將dex文件翻譯成oat文件,實際上就是將dex字節(jié)碼翻譯成本地機(jī)器碼,并且保存在一個oat文件中。
函數(shù)run_dexopt通過調(diào)用/system/bin/dexopt來對dex字節(jié)碼進(jìn)行優(yōu)化,而函數(shù)run_dex2oat通過調(diào)用/system/bin/dex2oat來將dex字節(jié)碼翻譯成本地機(jī)器碼。注意,無論是對dex字節(jié)碼進(jìn)行優(yōu)化,還是將dex字節(jié)碼翻譯成本地機(jī)器碼,最終得到的結(jié)果都是保存在相同名稱的一個odex文件里面的,但是前者對應(yīng)的是一個dey文件(表示這是一個優(yōu)化過的dex),后者對應(yīng)的是一個oat文件(實際上是一個自定義的elf文件,里面包含的都是本地機(jī)器指令)。通過這種方式,原來任何通過絕對路徑引用了該odex文件的代碼就都不需要修改了。

三.oat文件格式#

借助羅大神的圖我們可以知道,OAT文件本質(zhì)上是一個ELF文件,因此在最外層它具有一般ELF文件的結(jié)構(gòu),例如它有標(biāo)準(zhǔn)的ELF文件頭以及通過段(Section)來描述文件內(nèi)容。


Paste_Image.png

OAT文件包含有兩個特殊的段oatdata和oatexec,前者包含有用來生成本地機(jī)器指令的dex文件內(nèi)容,后者包含有生成的本地機(jī)器指令,它們之間的關(guān)系通過儲存在oatdata段前面的oat頭部描述。

APK安裝過程中生成的OAT文件的輸入只有一個DEX文件,也就是來自于打包在要安裝的APK文件里面的classes.dex文件。實際上,一個OAT文件是可以由若干個DEX生成的。這意味著在生成的OAT文件的oatdata段中,包含有多個DEX文件。詳細(xì)分析請移步Android運(yùn)行時ART加載OAT文件的過程分析

四.multidex加載odex,multidex和oat的關(guān)系

MultiDex在dalvik虛擬機(jī)上的簡要安裝過程:
將/data/app/apkName.apk路徑下解壓得到的classes2.dex, …, classesN.dex,依次寫入到/data/data/pkgName/code_cache/secondary-dexes/apkName.apk.classes2.zip等zip文件的classes.dex中,并返回這個zip列表。然后針對這個zip列表執(zhí)行安裝過程,具體過程是,將這個要安裝的zip列表加入BaseDexClassLoader的pathList實例的dexElements數(shù)組中,其中會針對各dex文件進(jìn)行dex2opt優(yōu)化。一旦加入到了dexElements數(shù)組中,程序啟動的時候,ClassLoader會加載dexElements數(shù)組中的元素,從而實現(xiàn)multi dex的安裝。

上面提到OAT文件可以由若干個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文件列表前會做一些校驗操作,其中包含檢查APK是否已安裝,若APK已安裝,則不進(jìn)行后續(xù)操作。檢查SDK版本號,版本號大于20不能保證MultiDex可正常Work

Set var2 = installedApk;
                synchronized(installedApk) {
                    String apkPath = e.sourceDir;
                    if(installedApk.contains(apkPath)) {
                        return;
                    }

所以multidex在ART上不會影響程序的邏輯,它和ART沒有關(guān)系~。。。。
multidex源碼分析:MultiDex安裝過程源碼分析

小結(jié):

從安裝過程上來看
Java的代碼實際上需要兩次“轉(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é)碼每次啟動都需要執(zhí)行將優(yōu)化過的odex字節(jié)碼再轉(zhuǎn)換成機(jī)器碼

ART優(yōu)缺####

系統(tǒng)性能大幅提升
App啟動、運(yùn)行更快
減少每次啟動的編譯增加電池續(xù)航
存儲占用更大
安裝時間更長

復(fù)制粘貼+理解,,,希望以后忘了能回來看看~!~


關(guān)注微信公眾號 Android歷練記 或掃一掃二維碼:
讓我們一起來搞事情。

Android歷練記
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評論 2 380

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