Android 9.0 ART編譯分析(三)-虛擬機觸發(fā)dex2oat編譯流程

原創(chuàng)內(nèi)容,轉(zhuǎn)載請注明出處,多謝配合。

這部分是針對動態(tài)加載插件的編譯,主要是由ClassLoader來觸發(fā)的,下面簡單總結(jié)下。

一、Android 的ClassLoder介紹
1.1 ClassLoder類

ClassLoader 抽象父類 。
BootClassLoader 加載Android 系統(tǒng)編譯文件。
BaseDexClassLoader PathClassLoader和DexClassLoader的父類,主要功能執(zhí)行者。
PathClassLoader 加載已被安裝的應(yīng)用路徑中.dex 文件。
DexClassLoader 加載指定路徑中的.dex 文件。
注:這里PathClassLoader和DexClassLoader都能加載外部dex, 只是DexClassLoader 可以指定 optimizedDirectory,也就是 dex2oat 的產(chǎn)物 .odex 存放的位置,而 PathClassLoader 只能使用系統(tǒng)默認位置。

1.2 初始化

getClassloader實際上就是選擇對應(yīng)classLoader并保證初始化的過程,常用的是Context去獲取的。

/frameworks/base/core/java/android/app/ContextImpl.java

@Override
public ClassLoader getClassLoader() {
 return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
}

不管是LoadedApk. getClassLoader 還是ClassLoader.getSystemClassLoader(),針對系統(tǒng)級別的類加載都是PathClassLoader。

1.3 DexClassLoader與PathClassLoader構(gòu)造方法說明

DexClassLoader與PathClassLoader均繼承BaseDexClassLoader,內(nèi)部只包含構(gòu)造方法,真正的功能實現(xiàn)都在BaseDexClassLoader。

兩者構(gòu)造方法解析:

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { / /熱修復(fù)使用這個
    super((String)null, (File)null, (String)null, (ClassLoader)null); 
   throw new RuntimeException("Stub!"); 
}
參數(shù):
dexPath:dex 文件路徑列表,多個路徑使用”:”分隔
optimizedDirectory:經(jīng)過優(yōu)化的 dex 文件(odex)文件輸出目錄
librarySearchPath:動態(tài)庫路徑(將被添加到 app 動態(tài)庫搜索路徑列表中)
parent:這是一個 ClassLoader,這個參數(shù)的主要作用是保留 java 中 ClassLoader 的委托機制。
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) { 
    super((String)null, (File)null, (String)null, (ClassLoader)null); 
   throw new RuntimeException("Stub!"); 
}
參數(shù):
dexPath:文件或者目錄的列表
librarySearchPath:包含 lib 庫的目錄列表
parent:父類加載器

PathClassLoader不能主動從zip包中釋放出dex,因此只支持直接操作dex格式文件,或者已經(jīng)安裝的apk(因為已經(jīng)安裝的apk在cache中存在緩存的dex文件)。而DexClassLoader可以支持.apk、.jar和.dex文件,并且會在指定的outpath路徑釋放出dex文件。

二、ClassLoader相關(guān)流程

先上流程圖


動態(tài)加載插件直接觸發(fā)dex2oat編譯流程

注:這里loadClass線路點到為止,因為它并不是本篇文章的重點。

兩條路線解析:
先走dex編譯,再走loadClass類加載。

1)6-17是makeDexElements 保存dexElements 并觸發(fā)dex編譯過程

DexPathList有一個Element[] dexElements; 是用來保存dex/resource(class path) 的集合,由makeDexElements來收集。makeDexElements 方法會遍歷所有dex path,并將它們一個個封裝為Element,保存到Element[]中,長度不夠通過copyOf翻倍擴容。另外會通過loadDexFile開始走編譯路線。
編譯的核心方法在oat_file_manager.cc中的OpenDexFilesFromOat,這里通過oat_file_assistant.cc 執(zhí)行isUpToDate判斷dex文件是否需要編譯,如果需要走它的MakeUpToDate方法,執(zhí)行編譯。
MakeUpToDate中如果需要執(zhí)行編譯會走GenerateOatFileNoChecks,最終調(diào)用其Dex2Oat方法,調(diào)整好參數(shù)傳給dex2oat這個執(zhí)行文件去Exec。

2)1-5是loadClass類加載過程

在ClassLoader中執(zhí)行classLoader:這里遵循雙親委派機制,先判斷當(dāng)前ClassLoader是否加載過,如果沒有就讓父類去加載,父類都不加載,再輪到子類去加載。
BaseDexClassLoader執(zhí)行findClass,實際是通過DexPathList去執(zhí)行findClass。
因為之前makeDexElements給Element[] dexElements賦值了,這里DexPathList執(zhí)行的findClass實際上是遍歷所有dexElements,每一個element都執(zhí)行findClass
而最終是交給DexFile去做的類加載:其具體流程不贅述,跟java ClassLoader差不多:
順序經(jīng)歷如下流程:

  • Loading:類的信息從文件中獲取并加載到JVM內(nèi)存中。
  • Verifying:檢查讀入的結(jié)構(gòu)是否符合JVM規(guī)范。
  • Preparing:驗證完正確,會分配一個結(jié)構(gòu)來存儲類信息。
  • Resolving: 把這個類的常量池中的所有符號引用轉(zhuǎn)變?yōu)橹苯右谩?/li>
  • Initializing:執(zhí)行靜態(tài)初始化程序,把靜態(tài)變量初始化成指定的值。

最終就是將dex字節(jié)碼文件loader到內(nèi)存,按虛擬機內(nèi)存劃分區(qū)域去存放對應(yīng)的數(shù)據(jù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容