ClassLoader
Introduction
- Java是半編譯半解釋語言获茬,任何一個.java文件(其實就是一個類文件)都要被jdk的編譯器編譯成class文件(Android上會把所有的class文件進(jìn)行一個合并,并對class文件做一些優(yōu)化,比如去除運(yùn)行時常亮池里重復(fù)的部分),class文件就是jvm的可執(zhí)行文件,任何一個class文件都必須要被JVM加載(就是一次IO,把class文件讀入內(nèi)存,然后JVM再去進(jìn)行相應(yīng)的處理)后才能在運(yùn)行時使用饰抒,一個class文件的加載還分幾個步驟:
- 裝載
- 等于一次IO
- 校驗
- 任何一個文件包括可執(zhí)行文件、class文件都是可以人為修改或者編寫的(只是可執(zhí)行文件和class文件等我們都是用編譯器來自動生成的)诀黍,所以就可能有一些不合法的指令
- 準(zhǔn)備
- 為靜態(tài)變量分配空間(加載的是類文件袋坑,又沒有對象,所以當(dāng)然只能是靜態(tài)變量了)眯勾,只是分配枣宫,沒有賦值動作
- 解析
- 官方說法:把符號引用解析為直接引用,簡單且非正式的解釋一下吃环,在class文件里也颤,java類的每一個方法和變量都是依靠字符串的形式存儲的,而不管怎么樣郁轻,我們最后肯定是要像C/C++一樣拿到方法或變量的地址才能進(jìn)行調(diào)用翅娶,這個解析就是做了一部分這樣的工作(說一部分的原因是因為多態(tài)的特性可能會導(dǎo)致在加載時不能確定具體的方法地址)
- 初始化
- 對靜態(tài)變量進(jìn)行賦值,執(zhí)行靜態(tài)方法塊里的操作好唯,這里的本質(zhì)其實也是javac在編譯時就收集好所有的靜態(tài)操作竭沫,生成一個自動執(zhí)行的靜態(tài)方法
- 裝載
- ClassLoader就是用來將class文件們正確加載到j(luò)vm的類,其實也就是實現(xiàn)了裝載這個功能
- 關(guān)于類加載和java類的本質(zhì)只是非正式不嚴(yán)謹(jǐn)?shù)闹v述了一下骑篙,因為這篇文章如果再講這些東西就太長了
Parent Delegate Model
其實這個模型就是一個委派(Delegate)模式:
- 當(dāng)VM需要去加載類文件(字節(jié)碼)時蜕提,會使用ClassLoader去把字節(jié)碼讀取到內(nèi)存中,而每一個ClassLoader在需要load時靶端,都會先嘗試使用自己的父類Loader去加載谎势,如果父類已經(jīng)加載過了這個Class或者執(zhí)行了加載操作,那子類就不必再去執(zhí)行加載操作
- 這樣的好處是可以避免重復(fù)加載
- 在JVM中杨名,ClassLoader類的委托層次
- Bootstrap ClassLoader: 最頂層的ClassLoader脏榆,負(fù)責(zé)加載JAVA_HOME/lib/下面的標(biāo)準(zhǔn)類庫,即如果不是Java的標(biāo)準(zhǔn)類台谍,那Bootstrap會忽略
- Extension ClassLoader: 負(fù)責(zé)加載JAVA_HOME/lib/ext/下面所有的類
- Application ClassLoader: 負(fù)責(zé)加載上述兩種自帶ClassLoader所不能加載的其他的CLASSPATH下的類姐霍,也就是說一般我們自己定義的類都是這個ClassLoader加載的
- 注意這些類沒有繼承層次,都是通過delegate去進(jìn)行調(diào)用的(也就是組合)
- 在Android的Dalvik/ART中:
- BootClassLoader:最頂層的ClassLoader,它相當(dāng)于JVM中的BootstrapClassLoader镊折,用來加載android的標(biāo)準(zhǔn)類庫
- BaseDexClassLoader:ClassLoader的子類,它是PathClassLoader和DexClassLoader的基類介衔,它不是一個abstract類恨胚,它的constructor提供了幾個參數(shù)
- dexPath: 加載的apk或者jar的路徑
- optimizedDirectory:從apk或者jar/aar提取出來的odex文件存放的路徑,null的話就會放在默認(rèn)路徑下
- libraryPath:C/C++庫的目錄
- parent:委托對象
- PathClassLoader:optimizedDirectory設(shè)置為null炎咖,即提取出來odex存放在默認(rèn)路徑赃泡,系統(tǒng)加載我們自己定義的類就是用的這個ClassLoader,相當(dāng)于ApplicationClassLoader乘盼,另外在art上貌似加載未安裝的apk是可以的升熊,但是在dalvik上是不可以的,這個classloader也不可以從網(wǎng)絡(luò)上加載
- DexClassLoader:optimizedDirectory是保留的绸栅,而且無論是在dalvik上還是art上都可以從任何地方加載odex文件级野,可以使用它加載我們指定路徑上的odex文件,包括從網(wǎng)絡(luò)上加載
How
defineClass是用來虛擬機(jī)用來將讀入的字節(jié)流轉(zhuǎn)化為Class對象的方法粹胯,即從磁盤上讀出一個class/dex文件后蓖柔,怎么樣把它轉(zhuǎn)化為對應(yīng)的Class對象存儲在對應(yīng)的永生代/元空間內(nèi),這個方法是Java為我們寫好的风纠;在 Android上我們要下載源碼才能看到這個方法况鸣,否則只是一個throw Exception
loadClass:這個方法定義了類加載的方式,Parent Delegate Model就是在loadClass里面制定的策略竹观,它會先確認(rèn)自己是否加載過這個類镐捧,然后再委托給parent,最后如果仍未加載臭增,自己嘗試加載(findClass)懂酱;如果想要自己定義類加載策略,可以O(shè)verride這個方法
findClass:定義了如何去尋找這個方法參數(shù)里指定的class速址,一般來說我們只需要重寫這個方法
-
定義好了如何使用:
ClassLoader loader = new MyClassLoader(...); loader.loadClass("TestClass");
?
Why玩焰?
- 我們可以動態(tài)的指定類加載的地址,比如我們通過網(wǎng)絡(luò)獲取了一個class/dex文件芍锚,在app里面把它存儲到設(shè)置好的地址昔园,然后就可以讓應(yīng)用程序加載使用這個類,這樣可以為應(yīng)用程序新增/改變行為(插件化開發(fā)和熱修復(fù))
- 可以加載不在ClassPath下的class文件