Android類加載(一)——DVM、ART、Dexopt、DexAot名詞解析
Android類加載(二)——雙親委托機(jī)制
Android類加載(三)——源碼解讀
Java類加載器
定義
- BootClassLoader
用于加載Android FrameWork層的class文件(系統(tǒng)的Activity) - PathClassLoader
用于加載Android應(yīng)用程序的class 文件(自己寫(xiě)的MainActivity)
也可以加載指定的dex,以及jar、zip、apk中的classes.dex - DexClassLoader
加載指定的dex,以及jar、zip、apk中的classes.dex
比較PathClassLoader和DexClassLoader
- 在一個(gè)app安裝的過(guò)程中,Android系統(tǒng)是沒(méi)有用到DexClassLoader類的,而PathClassLoader會(huì)被用于加載Android應(yīng)用程序的class 文件(例如自己寫(xiě)的MainActivity),DexClassLoader類就是提供給開(kāi)發(fā)者使用的類,Android FrameWork層并沒(méi)有使用到DexClassLoader。
- 構(gòu)造方法的區(qū)別
public class DexClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code DexClassLoader} that finds interpreted and native
* code. Interpreted classes are found in a set of DEX files contained
* in Jar or APK files.
*
* <p>The path lists are separated using the character specified by the
* {@code path.separator} system property, which defaults to {@code :}.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param optimizedDirectory directory where optimized dex files
* should be written; must not be {@code null}
* @param libraryPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be
* {@code null}
* @param parent the parent class loader
*/
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
public class PathClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code PathClassLoader} that operates on a given list of files
* and directories. This method is equivalent to calling
* {@link #PathClassLoader(String, String, ClassLoader)} with a
* {@code null} value for the second argument (see description there).
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param parent the parent class loader
*/
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
/**
* Creates a {@code PathClassLoader} that operates on two given
* lists of files and directories. The entries of the first list
* should be one of the following:
*
* <ul>
* <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
* well as arbitrary resources.
* <li>Raw ".dex" files (not inside a zip file).
* </ul>
*
* The entries of the second list should be directories containing
* native library files.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param libraryPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be
* {@code null}
* @param parent the parent class loader
*/
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
}
從源碼里可以看出它們都派生于BaseDexClassLoader類,但在它們的構(gòu)造方法稍有不同
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
有三個(gè)參數(shù)是一樣的
- dexPath:要加載的dex所在的目錄
- libraryPath:Native方法so文件所在的目錄
- parent:可以理解為雙親加載機(jī)制里的所謂的“父親”(雙親委托機(jī)制下面再講)
- optimizedDirectory:opt優(yōu)化后的dex文件所在的目錄(而且從源碼的注釋里可以看到,這個(gè)目錄必須為私有目錄,不能為sd卡的目錄)
而DexClassLoader就比PathClassLoader多了一個(gè)optimizedDirectory參數(shù),
也就是說(shuō)DexClassLoader用于存儲(chǔ)opt優(yōu)化后的dex文件的保存路徑可以自己定義傳進(jìn)去,而PathClassLoader存儲(chǔ)opt優(yōu)化后的dex文件的保存路徑是系統(tǒng)默認(rèn)的。僅此這個(gè)區(qū)別而已。
雙親委托機(jī)制
類加載在加載類時(shí),首先將加載任務(wù)委托給父類加載器加載,依次遞歸,如果父類加載器可以完成加載任務(wù),就成功返回,如果父類加載器無(wú)法完成或者沒(méi)有父類加載器時(shí),才自己去加載。
簡(jiǎn)單一個(gè)詞語(yǔ)概括:啃老族機(jī)制。
下面我們用一段代碼來(lái)進(jìn)行解釋:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//PathClassLoader
ClassLoader classLoader = this.getClassLoader();
//BootClassLoader
ClassLoader classLoader1 = Activity.class.getClassLoader();
System.out.println("PathClassLoader getClassLoader:"+classLoader);
System.out.println("PathClassLoader getClassLoader 的父親 :"+classLoader.getParent());
System.out.println("BootClassLoader Activity.class :"+classLoader1);

從打印中我們可以得出以下結(jié)論
- MainActivity的類加載器是PathClassLoader
- Activity的類加載器是PathClassLoader
- PathClassLoader的父親是BootClassLoader
注意:這里所說(shuō)的父親并不是指這個(gè)類的父類(BaseDexClassLoader),只是ClassLoder類里的一個(gè)類型為ClassLoader的成員變量名稱叫做parent


那么為什么要使用雙親委托機(jī)制呢?
在我們應(yīng)用程序啟動(dòng)的時(shí)候,BootClassLader會(huì)去加載FrameWork層的所有的類,例如上面的Activity,而我們自己寫(xiě)的代碼BootClassLader是沒(méi)辦法去加載的,只能由PathClassLoader去加載,因?yàn)槲覀冏约簩?xiě)的代碼的類,在我們的apk里面,不在系統(tǒng)里面,例如上面的MainActivity。但現(xiàn)在我們寫(xiě)的MainActivity是派生于AppCompatActivity的,AppCompatActivity屬于FrameWork層的類,已經(jīng)被BootClassLader加載了,如果我們不使用雙親委托機(jī)制的話,PathClassLoader是不是加載不到FrameWork層的類,那是不是加載不了AppCompatActivity了,而運(yùn)用雙親委托機(jī)制就能很好的讓不同的類加載器去分別執(zhí)行不同層級(jí)的類加載的任務(wù),這樣也是有利于系統(tǒng)的安全性,對(duì)于開(kāi)發(fā)者來(lái)說(shuō)你是無(wú)法知道怎么去加載系統(tǒng)的類的,你永遠(yuǎn)只能加載自己寫(xiě)的類,系統(tǒng)層面的類是不讓你去加載的,只能由父親BootClassLader去加載。