這篇文章卧须,最基本的理論開始,最后講遇到問題解決方法
第一部分:odex vdex art 文件
vdex
- package 直接轉(zhuǎn)化的 可執(zhí)行二進(jìn)制碼 文件:
- 第一次開機(jī)就會生成在/system/app/<packagename>/oat/ 下获搏;
- 在系統(tǒng)運行過程中,虛擬機(jī)將其 從 “/system/app” 下 copy 到 “/data/davilk-cache/” 下
odex
- odex 是從vdex 這個文件中 提取了部分模塊生成的一個新的 可執(zhí)行二進(jìn)制碼 文件 , odex 從vdex 中提取后,vdex 的大小就減少了呻逆。
- 第一次開機(jī)就會生成在/system/app/<packagename>/oat/ 下
在系統(tǒng)運行過程中夸赫,虛擬機(jī)將其 從 “/system/app” 下 copy 到 “/data/davilk-cache/” 下 - odex + vdex = apk 的全部源碼
art
- odex 進(jìn)行優(yōu)化 生成的 可執(zhí)行二進(jìn)制碼 文件菩帝,主要是apk 啟動的熱點函數(shù)相關(guān)地址的記錄,方便尋址相關(guān)茬腿;
- 第一次開機(jī)不會生成在/system/app/<packagename>/oat/ 下呼奢,以后也不會;
- odex 文件在運行時切平,虛擬機(jī)會計算函數(shù)調(diào)用頻率握础,進(jìn)行函數(shù)地址的修改;如果頻率比較多悴品,就最后在/data/davilk-cache/ 由虛擬機(jī)生成;生成art 文件后昔脯,/system/app 下的odex 和 vdex 會無效桦沉,即使你刪除,apk也會正常運行
- push 一個新的apk file 覆蓋之前/system/app 下apk file 孤澎,會觸發(fā)PKMS 掃描時下發(fā)force_dex flag ,強(qiáng)行生成新的vdex 文件 欠窒,覆蓋之前的vdex 文件覆旭,由于某種機(jī)制,這個新vdex 文件會copy到/data/dalvik-cache/下岖妄,于是art 文件也變化了型将。
第二部分:JVM DVM ART
JVM:JVM虛擬機(jī)運行的是java字節(jié)碼。java -> java bytecode(class) -> java bytecode(jar)荐虐。
DVM:DVM虛擬機(jī)解析執(zhí)行的dex字節(jié)碼七兜。java -> java bytecode(class) -> dalvik bytecode(dex)。
ART:ART虛擬機(jī)執(zhí)行本地機(jī)器碼福扬。java -> java bytecode(class) -> dalvik bytecode(dex) -> optimized android runtime machine code(oat->art)惊搏。
Android--<-Android 4.4-DVM-->ART-->Android 5.0--ART->Android 7.1-> DVM(JIT)&ART---->now
ART所使用的AOT(Ahead-Of-Time)編譯,在應(yīng)用首次安裝時忧换,字節(jié)碼預(yù)編譯成機(jī)器碼存在本地
DVM是典型的JIT(Just-In-Time)恬惯,在此模式下,應(yīng)用每次運行的時候亚茬,字節(jié)碼都需要即時編譯器轉(zhuǎn)換為機(jī)器碼再執(zhí)行
第三部分 ART 的運作方式
ART 官方說明
ART 使用預(yù)先 (AOT) 編譯酪耳,并且從 Android 7.0(代號 Nougat,簡稱 N)開始結(jié)合使用 AOT刹缝、即時 (JIT) 編譯和配置文件引導(dǎo)型編譯碗暗。所有這些編譯模式的組合均可配置,我們將在本部分中對此進(jìn)行介紹梢夯。例如言疗,Pixel 設(shè)備配置了以下編譯流程:
最初安裝應(yīng)用時不進(jìn)行任何 AOT 編譯。應(yīng)用前幾次運行時颂砸,系統(tǒng)會對其進(jìn)行解譯噪奄,并對經(jīng)常執(zhí)行的方法進(jìn)行 JIT 編譯。
當(dāng)設(shè)備閑置和充電時人乓,編譯守護(hù)進(jìn)程會運行勤篮,以便根據(jù)在應(yīng)用前幾次運行期間生成的配置文件對常用代碼進(jìn)行 AOT 編譯。
下一次重新啟動應(yīng)用時將會使用配置文件引導(dǎo)型代碼色罚,并避免在運行時對已經(jīng)過編譯的方法進(jìn)行 JIT 編譯碰缔。在應(yīng)用后續(xù)運行期間進(jìn)行了 JIT 編譯的方法將會被添加到配置文件中,然后編譯守護(hù)進(jìn)程將會對這些方法進(jìn)行 AOT 編譯戳护。
ART 包括一個編譯器(dex2oat 工具)和一個為啟動 Zygote 而加載的運行時 (libart.so)金抡。dex2oat 工具接受一個 APK 文件瀑焦,并生成一個或多個編譯工件文件,然后運行時將會加載這些文件梗肝。文件的個數(shù)蝠猬、擴(kuò)展名和名稱會因版本而異,但在 Android O 版本中统捶,將會生成以下文件:
* .vdex:其中包含 APK 的未壓縮 DEX 代碼榆芦,另外還有一些旨在加快驗證速度的元數(shù)據(jù)。
* .odex:其中包含 APK 中已經(jīng)過 AOT 編譯的方法代碼喘鸟。
* .art (optional):其中包含 APK 中列出的某些字符串和類的 ART 內(nèi)部表示匆绣,用于加快應(yīng)用啟動速度。
第四部分:運行機(jī)制
DVM
運行時中什黑,APK在安裝的時候崎淳,安裝服務(wù)PackageManagerService會通過守護(hù)進(jìn)程installd調(diào)用一個工具 dexopt
對打包在APK里面包含有Dex字節(jié)碼的classes.dex進(jìn)行優(yōu)化,優(yōu)化得到的文件保存在/data/dalvik-cache目錄 中愕把,并且以.odex為后綴名拣凹,表示這是一個優(yōu)化過的Dex文件。
ART
運行時中恨豁,APK在安裝的時候嚣镜,同樣安裝服務(wù) PackageManager Service會通過守護(hù)進(jìn)程installd調(diào)用另外一個工具dex2oat
對打包在APK里面包含有Dex字節(jié)碼進(jìn)翻譯。翻譯后得到的是一個ELF格式的oat文件橘蜜,這個oat文件同樣是以.odex后綴結(jié)束菊匿,并且也是保存在/data/dalvik-cache目錄中。
Android在首次啟動和首次安裝應(yīng)用時计福,需要將字節(jié)碼翻譯成機(jī)器碼跌捆,這樣Android系統(tǒng)的啟動速度將會大大減慢,如果沒有預(yù)優(yōu)化象颖,APP的運行速度也會加上翻譯所需要的時間佩厚。所以,這個翻譯的工作需要轉(zhuǎn)移到編譯上面來说订,也就是所抄瓦,在編譯APK文件時,將會預(yù)先對APK進(jìn)行翻譯的優(yōu)化克蚂,然后再打包到系統(tǒng)里面去闺鲸,這樣Android系統(tǒng)在首次啟動時筋讨,就不再需要花費大量的時間去翻譯APK的字節(jié)碼埃叭。
第五部分:遇到問題
03-12 10:48:50.247 1381 1381 E AndroidRuntime: Process: com.android.settings, PID: 1381
03-12 10:48:50.247 1381 1381 E AndroidRuntime: java.lang.RuntimeException: Unable to instantiate application com.android.settings.SettingsApplication: java.lang.ClassNotFoundException: Didn’t find class “com.android.settings.SettingsApplication” on path: DexPathList[[zip file “/system/priv-app/Settings/Settings.apk”],nativeLibraryDirectories=[/system/priv-app/Settings/lib/arm64, /system/priv-app/Settings/Settings.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib, /system/lib, /vendor/lib]]
下面指令查看 系統(tǒng)安裝的package信息
adb shell dumpsys package p > dumpsys_p
注意出錯的apk:primaryCpuAbi=arm64-v8a
,是64 bit apk , 因此尋找了32 bit的庫
apk架構(gòu)確定順序:
- 如果apk包中l(wèi)ib文件夾下有.so庫悉罕,就根據(jù)這個.so庫的架構(gòu)模式赤屋,確定app的primaryCpuAbi的值
- 對于system app, 如果沒法通過第一步確定primaryCpuAbi的值立镶,PKMS會根據(jù)
/system/app/${APP_NAME}/lib和/system/app/${APP_NAME}/lib64
這兩個文件夾是否存在,來確定它的primaryCpuAbi的值 - 對于還沒有確定的app, 在最后還會將自己的primaryCpuAbi值與和他使用相同UID的package的值設(shè)成一樣
- 對于到這里還沒有確認(rèn)primaryCpuAbi的app类早,就會在啟動進(jìn)程時使用ro.product.cpu.abilist這個property的值的第一項作為它關(guān)聯(lián)的ABI
解決方法:
基于android加載so文件的機(jī)制媚媒,我們可以采用一下幾種方法來解決該問題
- apk不使用系統(tǒng)的uid,自己新增一個具有系統(tǒng)權(quán)限的uid(比較麻煩)
- 把我們的apk修改為64位的涩僻,將所有使用的32位so替換為64位(可能有些三方so不提供64位版本
- 把所有32位庫再封裝一次缭召,做成jar包,然后使用這些jar包逆日。(需要自己封裝)
將所有32位庫服務(wù)抽離出來嵌巷,單獨運行一個(或多個)進(jìn)程。(需要提前確定好app的架構(gòu)室抽,不然需要重構(gòu)app) - 在系統(tǒng)apk(例如:Settings)安裝位置system/priv-app/xxx/目錄下面建立一個lib/arm64文件夾.(利用apk架構(gòu)確定順序強(qiáng)制將系統(tǒng)apk變?yōu)?4位)
- 將系統(tǒng)apk編譯 不剝離 dex 模式
a.在apk對應(yīng)的android.mk中LOCAL_DEX_PREOPT := nostripping
b./device/xxx/xxx/BoardConfig.mk,DEX_PREOPT_DEFAULT := nostripping
其他問題搪哪,在實際的解決問題的時候要考慮系統(tǒng)簽名,API 版本問題
REF:
https://www.cnblogs.com/blogs-of-lxl/p/11608115.html
https://zhuanlan.zhihu.com/p/104122672
http://www.reibang.com/p/18a8a4e6af3f