一、Android類加載器
虛擬機加載類時,將二進(jìn)制字節(jié)流讀取到內(nèi)存竞惋,可以從class文件柜去、zip包、jar包拆宛、網(wǎng)絡(luò)或動態(tài)代理生成二進(jìn)制字節(jié)流嗓奢。
某一個類,需要保證在虛擬機中的唯一性浑厚,由類加載器和他本身決定股耽,不同的類加載器加載的類肯定不等,相同的類加載器保證某個類只加載一次钳幅。
Android類加載器
PathClassLoader物蝙,DexClassLoader,BootClassLoader贡这。
ClassLoader茬末,抽象類,定義了類加載的基礎(chǔ)方法loadClass盖矫,findClass丽惭。
BootClassLoader,加載系統(tǒng)Sdk框架類辈双,系統(tǒng)啟動時創(chuàng)建责掏,如Context,Activity類湃望。
Activity.class.getClassLoader();
String.class.getClassLoader();
輸出結(jié)果是BootClassLoader换衬,調(diào)用Class類的getClassLoader方法,這些系統(tǒng)類的加載器都是BootClassLoader证芭。
BaseDexClassLoader類瞳浦,父類,路徑都在dalvik.system包中废士。
PathClassLoader叫潦,應(yīng)用啟動時創(chuàng)建一個實例,加載系統(tǒng)內(nèi)已安裝apk中的類官硝。
MainActivity.class.getClassLoader();
MainActivity.class.getClassLoader()矗蕊,輸出結(jié)果PathClassLoader,自定義類的類加載器氢架。
Context類內(nèi)部的getClassLoader加載器傻咖,PathClassLoader。
DexClassLoader岖研,加載未安裝apk中的類卿操,dex文件,在熱修復(fù)/插件化時可以使用,加載外部存儲路徑下的apk或dex補丁硬纤。
二解滓、雙親委派機制
類加載時默認(rèn)采用雙親委派機制,當(dāng)類加載器收到加載任務(wù)筝家,總是先將任務(wù)委派給內(nèi)部的父類加載器洼裤,(內(nèi)部包含父類加載器),直到將請求傳遞到最頂層的啟動類加載器溪王,如果成功加載腮鞍,返回通知結(jié)果,如果失敗則由下層加載莹菱。
只有當(dāng)父類無法完成加載請求時移国,子加載器加載。
BootClassLoader是DexClassLoader和PathClassLoader的父加載器道伟。
1迹缀,原理
ClassLoader的loadClass()方法。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
//先檢查是否已加載蜜徽。
Class<?> c = findLoadedClass(name);
if (c == null) {
//父加載器先找
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
//子類重寫查找
if (c == null) {
c = findClass(name);
}
}
return c;
}
判斷是否已加載class祝懂,未加載,先parent去加載拘鞋,遞歸調(diào)用loadClass()砚蓬,
如果parent是空,是頂層BootClassLoader盆色,重寫loadClass()灰蛙,直接查找findClass()。
如果parent加載不成功隔躲,findClass()自己加載摩梧。
BaseDexClassLoader,重寫findClass()宣旱。
自定義ClassLoader仅父,同上,重寫findClass()响鹃,不需要改變loadClass的基礎(chǔ)邏輯驾霜。
loadClass()案训,優(yōu)先委派父加載器买置,當(dāng)父加載器不能成功加載時,調(diào)用自己重寫當(dāng)findClass()自己加載强霎。
BootClassLoader重寫的loadClass方法忿项。
@Override
protected Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
clazz = findClass(className);
}
return clazz;
}
不需要請求父加載,本身就在頂層,直接findClass()轩触,BootClassLoader重寫的findClass()方法寞酿。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return Class.classForName(name, false, null);
}
通過Class類的classForName方法加載。ClassLoader類的findClass()是空方法脱柱,需要子類實現(xiàn)伐弹。
2,BaseDexClassLoader構(gòu)造參數(shù)
dexPath榨为,dex路徑惨好,DexClassLoader是dex文件或jar包的路徑,可以多個随闺,PathClassLoader類是apk的安裝路徑日川。
optimizedDirectory,dex文件被加載后會被編譯器優(yōu)化矩乐,優(yōu)化之后的dex存放路徑龄句,經(jīng)過odex優(yōu)化過的將apk壓縮包里的dex提取出來變成odex文件,這樣第一次啟動就不用解壓縮包散罕。當(dāng)加載app時分歇,已經(jīng)將apk安裝在本地文件系統(tǒng),并且內(nèi)部dex應(yīng)被提取并執(zhí)行過優(yōu)化笨使。
當(dāng)PathClassLoader加載時卿樱,會將其解壓到固定的/data/dalvik-cache目錄,所以硫椰,不需要指定該目錄繁调,是空。
當(dāng)DexClassLoader加載時靶草,非apk安裝的dex文件蹄胰,將dex優(yōu)化解壓到該目錄下,必須是應(yīng)用私有的可寫路徑奕翔,且不能是空裕寨,防止App被注入攻擊,可以將熱修復(fù)包或動態(tài)插件下載到私有路徑中派继,自定義繼承DexClassLoader加載器指定下載路徑宾袜。
ClassLoader parent,app啟動時驾窟,將系統(tǒng)創(chuàng)建的BootClassLoader傳入庆猫,它是PathClassLoader類加載器的父加載器。
PathClassLoader和DexClassLoader區(qū)別在于是否指定了optimizedDirectory绅络,PathClassLoader不指定月培。
dexpath目前只支持.dex嘁字、.jar、.apk杉畜、.zip格式纪蜒。
dexElements,DexPathList的Element數(shù)組此叠,根據(jù)dexPath路徑纯续,(通過:
連接多個dex或apk路徑)保存分割后的對應(yīng)Flie創(chuàng)建的Elememt,由File和DexFile組成灭袁,DexFile使用native方法openDexFile打開了具體的file并輸出到優(yōu)化路徑杆烁。
BaseDexClassLoader的findClass()方法。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
...
}
return c;
}
DexPathList類简卧,分離的每一個dex加載路徑存儲Element兔魂,數(shù)組,加載時举娩,遍歷Elements數(shù)組析校。
3,雙親委派機制的作用
共享功能铜涉,一些framework層級的類一旦被頂層加載器加載智玻,緩存在內(nèi)存。在其他任何地方用到時芙代,都遵守雙親加載機制吊奢,派發(fā)到頂層加載器,因已經(jīng)加載纹烹,所以都不需要重新加載页滚。
隔離功能,保證核心類庫的純凈和安全铺呵,防止惡意加載裹驰。
雙親委派機制在很大程度上防止內(nèi)存中出現(xiàn)多個相同的字節(jié)碼文件,加載類的時候默認(rèn)會使用當(dāng)前類的ClassLoader進(jìn)行加載片挂,只有當(dāng)你使用該class的時候才會去裝載幻林,一個加載器只會裝載同一個class一次∫裟睿可以通過A.class.getClassLoader查看當(dāng)前A類的加載器沪饺。
任重而道遠(yuǎn)