詳解dex優化對Arouter查找路徑的影響

一、前言

疑問:dex文件是什麼?dex文件優化又是什麼?

dex文件優化會給項目帶來什麼問題,怎麼解決這些問題?

1.1 APK的編譯和打包流程

1、通過aapt打包資源文件res,對應生成R.java、resources.arsc和res文件(二進制&非二進制保持原來的代碼)

2、處理aidl文件,生成java接口文件(沒有aidl則忽略)

3、通過java compile編譯R.java、java接口文件,生成對應.class文件(java compiler)

4、運用dex命令,將.class文件和第三方sdk庫中的.class文件轉換成classes.dex文件

5、通過apkbuilder將aapt生成的CompiledResources和其他資源文件以及classes.dex文件打包生成apk

6、同樣的,可以使用Jarsigner工具,對上面的apk進行debug或者release簽名

apk的編譯和打包流程圖如下:

實際項目開發中,5、6兩個步驟,可以借助jenkins平臺直接生成release包即可滿足需求。

1.2 dex文件的應用場景

再來看看dex文件常用的場景,比較流行的有:APK 的瘦身、熱修復、插件化、應用加固、Android 逆向工程、64 K 方法數限制。

拿方法數限制舉例,在上面的第4步,將class文件轉換成dex文件,默認隻會生成一個dex文件,單個dex文件中的方法數不能超過65536,不然編譯會報錯,但是我們在開發App時肯定會集成一堆庫,方法數一般都是超過65536的,解決這個問題的辦法就是:一個dex裝不下,用多個dex來裝,gradle增加一行配置:multiDexEnabled true。

dex文件的應用場景網上介紹的很多,本文不做介紹。而是對項目中實際遇到的問題進行剖析,從而對dex優化有進一步的理解。

二、dex到vdex、odex

2.1 ART預優化

ART(Android runtime)是什麼,它是虛擬機,運營java代碼、APP用的。參考Android發展史,Android 5.0把ART作為默認的虛擬機,而不是Android4.4及之前使用的DVM(Dalvik VM)瞭。

回顧一下DVM和ART和Android的關系,我們先來瞭解運行Java的幾種虛擬機的工作機制:(1)JVM:JVM虛擬機運行的是java字節碼。Java文件到JVM的過程是:java -> java bytecode(class) -> java bytecode(jar)

DVM:DVM虛擬機解析執行的dex字節碼。Java文件到DVM的過程是:java -> java bytecode(class) -> dalvik bytecode(dex)

ART:ART虛擬機執行本地機器碼。Java文件到ART的過程是:java -> java bytecode(class) -> dalvik bytecode(dex) -> optimized android runtime machine code(oat)

可以看到,DVM到ART的演變,實際上是java文件到虛擬機的執行代碼的過渡,相對而言,ART多瞭oat的過程,ART使用AOT(Ahead-Of-Time)編譯,在應用第一次安裝的時候,字節碼預編譯成機器碼存在本地,DVM是使用JIT(Just-In-Time)編譯,在應用每次運行的時候,字節碼都需要通過編譯器即時轉換為機器碼才能繼續執行。ART相對於DVM,省去瞭每次解析字節碼的過程,所以運行時占用的內存會減少,提升應用的運行效率。

2.2 ART的運行方式

ART在Android5.0時代,號稱使用AOT即可讓系統運行在512M的機器上。從 Android 7.0(簡稱 N)開始,ART結合 AOT、即時(JIT)編譯和配置文件引導型編譯。

這幾種模式可以組合配置,以谷歌的Pixel 設備舉例,配置瞭以下編譯流程:

1)最初安裝應用時不進行任何 AOT 編譯。應用前幾次運行時,系統會對其進行解譯,並對經常執行的方法進行 JIT 編譯。

2)當設備閑置和充電時,編譯守護進程會運行,以便根據在應用前幾次運行期間生成的配置文件對常用代碼進行 AOT 編譯。

下一次重新啟動應用時將會使用配置文件引導型代碼,並避免在運行時對已經編譯過的方法進行 JIT 編譯。在應用後續運行期間進行瞭 JIT 編譯的方法將會被添加到配置文件中,然後編譯守護進程將會對這些方法進行 AOT 編譯。

ART 包括一個編譯器(dex2oat 工具)和一個為啟動 Zygote 而加載的運行時(libart.so)。dex2oat 工具接受一個 APK 文件,並生成一個或多個編譯文件,然後運行時將會加載這些文件。文件的個數、擴展名和名稱會因版本而異,但在 Android O 版本中,將會生成以下文件:

vdex:其中包含 APK 的未壓縮 DEX 代碼,另外還有一些旨在加快驗證速度的元數據。

odex:其中包含 APK 中經過 AOT 編譯的方法代碼。

art (optional):其中包含 APK 中列出的某些字符串和類的 ART 內部表示,用於加快應用啟動速度。

2.3 vdex、odex的作用

解壓一個APK(以廠商的系統應用包舉例)的包,可以看到下面的結構,不含有任何dex文件

再看下這個應用在手機中的目錄結構,vdex、odex文件包含apk的所有代碼,正常也會包含classes.dex文件。由於vdex、odex是機器碼,沒辦法直接轉成可以查看的二級制碼查看(也可能是我使用的工具不對)。

2.4 vdex、odex與classes.dex關系

可能是系統編譯的bug,也可能是生成瞭ART文件之後,對odex、vdex文件做瞭二次處理,現象是這樣的,嘗試獲取odex中的dex文件,提示不含有dex文件。

為瞭再次確認odex裡面是否真的含有dex文件,使用010Editor再次確認,可以看到recent Files下面仍然是沒有dex文件的。

嘗試獲取vdex中的dex文件,也是無法獲取的。

所以說,odex(或者vdex)中含有classes.dex的說法是不正確的。

三、Arouter是什麼

阿裡的一個路由組件,功能很多,我這邊的實際使用場景是進行頁面跳轉。具體功能可以參考阿裡峰會上對arouter的介紹。

借鑒峰會中提到的一點作為鋪墊,也是我們下面將要講述的一點。“最後想分享的就是ARouter的未來開發計劃。未來ARouter會支持插件化並且支持生成映射關系文檔,因為插件化是現在很多大型APP中會使用的技術方案,很多的Dex和功能是動態地下發到APP中的,而在這種情況下,是無法找到所有的Dex文件的,也就是對於沒有加載過的Dex而言,裡面的映射關系是跳轉不過去的,所以一旦Dex文件位置發生變動,常規的方案是無法找到Dex的,也不能實現映射文件初始化,這一部分會在後面的版本中進行支持”。

阿裡可以識別的arouter路徑如下:

換句話說,arouter可能因為dex文件的位置變化或者路徑變化,而無法找到。

四、踩坑

4.1 現象

2.4中提到瞭odex文件中不含有dex,而arouter查找路徑遵循分組按需加載的規則,歸結到底,實際上就是對class文件的查找,如下圖:

而class文件的信息記錄在dex文件中,所以出現瞭異常,使用arouter進行頁面跳轉的時候,出現classNotFound exception。

4.2 解決方案

想要找到解決方案,就要知道怎麼樣讓odex對arouter路徑不產生影響,這方面,可能在沒有相關經驗的時候,很難找到解決方案,隻能一點點查找。通過搜索ART的工作原理,找到文章《配置ART》,其中文章提到:

也就是說通過配置LOCAL_DEX_PREOPT的屬性,可以防止odex優化,於是找到Android.mk中設置該屬性的地方進行設置LOCAL_DEX_PREOPT := nostripping。

既在編譯的時候做dex優化(生成odex文件),又不從apk裡剝離dex。於是有瞭下面的apk生成之後的路徑對比,再看下dex不被剝離的路徑,下面含有瞭classes.dex文件。

使用jadx打開這個classes.dex文件,發現arouter的路徑文件就在這裡,所以arouter的跳轉正常瞭,異常不再出現。

五、總結

odex優化這種系統做的事情,往往會出現一些意想不到的結果,如果你負責廠商的應用,經常需要內置項目,這時候要註意瞭,當你的應用中含有第三方框架的時候,要註意路徑、資源的引用都是沒問題的,雖然正常情況下,odex文件不會對你的路徑產生幹擾,但是也難免odex出現失誤,因為對於odex來說,裡面的資源無需保存,生成art文件能夠運行即可。合理使用art的配置,可以幫助解決很多問題。

以上就是詳解dex優化對Arouter查找路徑的影響的詳細內容,更多關於dex優化 Arouter的資料請關註WalkonNet其它相關文章!

推薦閱讀: