原創(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)流程
先上流程圖

注:這里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ù)。