類加載原理做爲程序運行的基礎,一直在程序的背後默默的付出。現在Android
中的插件化、熱修復等動態加載技術的實現也都涉及到了類加載的原理。關於類加載的相關知識我之前也是遇到一點看一點,沒有完整的詳細的瞭解過,最近有時間專門對這塊知識進行了學習,因而這裏作一個總結。java
一個類從.class
文件被加載到內存,到在內存中使用,最後從內存中卸載,這是一個完整的生命週期過程。不過在得到.class
文件以前,咱們編碼時的文件格式仍是.java
文件格式,還記得剛學Java
時學到過在完成編碼以後要先執行javac
命令進行編譯,編譯生成對應的.class
文件,以後再經過java
命令執行Java
程序。不過當時只知道是先編譯再運行,並不知道究竟是怎麼運行的誰去運行的。程序員
其實一個類從.class
文件被加載到內存到從內存中卸載,整個生命週期一共通過如下幾個階段:數組
在加載階段虛擬機主要完成如下三件事情:安全
java.lang.Class
對象,做爲方法區這個類的各類數據的訪問入口。這個簡單的來講加載階段主要就是將類的.class
文件做爲二進制字節流讀入內存,而且在內存中實例化一個java.lang.Class
對象以便後續訪問。在這個階段中.class
文件二進制字節流的讀取來源沒有太多限制,能夠很是靈活。好比能夠從本地系統中讀取、能夠從jar
包中讀取、能夠從網絡下載等等。bash
驗證是鏈接階段中的第一步,主要的做用是保證Class
文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機的自身安全。在驗證階段大體會完成如下四個檢驗操做:cookie
Class
文件格式的規範,而且能被當前版本的虛擬機處理。Java
語言規範的要求。從以上幾個操做能夠看出,這個階段主要就是將二進制字節流進行一個合法驗證,包括文件格式、語義、數據流控制流和符號引用等。保證不會出現相似文件格式錯誤、繼承了被final
修飾的類、指令跳轉錯誤、類型轉換錯誤、修飾符訪問性等等錯誤狀況。網絡
準備階段中主要是爲類中靜態變量在方法區裏分配內存而且設置類變量的初始值。這裏的初始值即零值。具體以下:數據結構
數據類型 | 零值 |
---|---|
int | 0 |
long | 0L |
short | (short)0 |
char | '\u0000' |
byte | (byte)0 |
boolean | false |
float | 0.0f |
double | 0.0d |
reference | null |
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。解析動做主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調試限定符7類符號引用進行。app
初始化階段中真正開始執行類中定義的Java
程序代碼,爲全部類變量進行賦值,執行靜態代碼塊。dom
從上面的類加載過程能夠看出在初始化階段之前除了加載階段,其他階段都是由虛擬機主導控制。而在加載階段能夠經過自定義類加載器進行參與。類加載器顧名思義是用來加載類的,負責將類的.class
文件轉換成內存中類的二進制字節流。Java
中的類加載器按類型分有兩種:系統類加載器和自定義類加載器。其中系統類加載器有主要有三種,分別是:引導類加載器(Bootstrap ClassLoader
)、拓展類加載器(Extensions ClassLoader
)和應用程序類加載器(Application ClassLoader
)。
Bootstrap ClassLoader
)這個類加載器是用C/C++
語言實現的,用來加載JDK
中的核心類,主要加載$JAVA_HOME/jre/lib
目錄下的類,例如rt.jar
、resources.jar
等包中的類。
Extensions ClassLoader
)這個類加載器是用Java
語言實現的,實現類爲ExtClassLoader
,用來加載Java
的拓展類,主要加載$JAVA_HOME/jre/lib/ext
目錄和系統屬性java.ext.dir
所指定的目錄。
Application ClassLoader
)這個類加載器是用Java
語言實現的,實現類爲AppClassLoader
,能夠經過ClassLoader.getSystemClassLoader
方法獲取到,主要加載Classpath
目錄和系統屬性java.class.path
指定的目錄下的類。
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
while (classLoader != null) {
System.out.println("ClassLoader:" + classLoader);
classLoader = classLoader.getParent();
}
}
}
複製代碼
這裏新建一個Java
類ClassLoaderDemo
,循環打印其類加載器和父類加載器,控制檯輸出結果以下。
從結果能夠看出ClassLoaderDemo
類的類加載器是AppClassLoader
,AppClassLoader
的父類加載器是ExtClassLoader
,而ExtClassLoader
的父類加載器就爲null
了,這是由於ExtClassLoader
的父類加載器BootstrapClassLoader
是由C/C++
語言實現的,因此在Java
中沒法獲取到它的引用。接下來再進入源碼來看一下,先看AppClassLoader
。
static class AppClassLoader extends URLClassLoader {
......
}
複製代碼
查看源碼發現AppClassLoader
的父類並非ExtClassLoader
而是URLClassLoader
,再看ExtClassLoader
。
static class ExtClassLoader extends URLClassLoader {
......
}
複製代碼
ExtClassLoader
的父類也是URLClassLoader
進而再看URLClassLoader
。
public class URLClassLoader extends SecureClassLoader implements Closeable {
......
}
public class SecureClassLoader extends ClassLoader {
......
}
public abstract class ClassLoader {
......
}
複製代碼
URLClassLoader
的父類是SecureClassLoader
,而SecureClassLoader
的父類是ClassLoader
,ClassLoader
是一個抽象類。經過對源碼的跟蹤發現,彷佛這裏的繼承關係與控制檯輸出的結果不太一致。因而進一步去看輸出ClassLoaderDemo
中調用的ClassLoader.getParent()
方法源碼。
public final ClassLoader getParent() {
if (parent == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(parent, Reflection.getCallerClass());
}
return parent;
}
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
複製代碼
ClassLoader
的getParent
方法中看到返回的是一個成員變量中的parent
,他是一個ClassLoader
類型對象。繼續跟蹤尋找它是在哪裏初始化賦值的。
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
複製代碼
跟蹤查看源碼發現父類加載器parent
是在ClassLoader
的構造函數時傳入的,若是沒有傳入默認調用getSystemClassLoader
方法獲取一個父類加載器。接下來繼續查看getSystemClassLoader
方法。
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
複製代碼
getSystemClassLoader
方法中又調用了initSystemClassLoader
方法初始化系統類加載器,方法最後將這個類加載器scl
返回。繼續查看initSystemClassLoader
方法。
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader();
.......
}
複製代碼
這個方法中獲取到Launcher
以後調用了Launcher
的getClassLoader
方法獲取到建立的類加載器,因而再到Launcher
中查看。
public ClassLoader getClassLoader() {
return this.loader;
}
複製代碼
Launcher
的getClassLoader
方法中返回了其成員變量中的loader
對象,因而再去尋找這個對象的建立。
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
......
}
複製代碼
在Launcher
的構造函數裏找到,這裏是經過Launcher.AppClassLoader.getAppClassLoader(var1)
方法建立的loader
,因而再進入查看。
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
複製代碼
getAppClassLoader
方法中最終new
了一個AppClassLoader
返回,這就又回到了AppClassLoader
的構造方法。以前看過構造傳入的第二個參數就是parent
父類加載器,這裏傳入的var0
能夠看到就是ExtClassLoader
類型。
總結來講ClassLoader
的父子關係並非由繼承實現的,A
是B
的父類加載器,並不表示B
繼承了A
,每一個CLassLoader
中保存了一個它的父類加載器的引用,經過getParent
方法得到的就是它的值,它是在類加載器建立時構造函數中進行賦值的。若是構造中沒有傳入父類加載器默認調用getSystemClassLoader
方法獲取系統類加載器,經過查看發現默認系統類加載器就是AppClassLoader
。在Launcher
的構造方法中,會依次建立ExtClassLoader
和AppClassLoader
,此時ExtClassLoader
做爲父類加載器由構造函數傳入AppClassLoader
。實際的類加載繼承關係以下圖。
在一些特殊需求場景下可能須要程序員自定義類加載器。例如從網絡下載一個加密過的.class
類文件,此時就須要自定義類加載器,先進行文件解密再進行類加載。下面就來模擬一下這個例子,先定義一個測試類TestPrint
。
public class TestPrint {
public void printString() {
System.out.println("測試輸出字符串");
}
}
複製代碼
使用javac
命令編譯生成TestPrint.class
文件。
Base64
加密,將編譯生成的
.class
文件轉成二進制字節流加密後再保存成本地文件。
public class Test {
public static void main(String[] args) {
byte[] classBytes = FileIOUtils.readFile2BytesByStream("/Users/sy/Downloads/ClassLoader/TestPrint.class");
FileIOUtils.writeFileFromBytesByStream("/Users/sy/Downloads/ClassLoader/TestPrint.class",Base64.getEncoder().encode(classBytes));
}
}
複製代碼
獲得加密後的TestPrint.class
後接下來編寫自定義的類加載器MyClassLoader
。
public class MyClassLoader extends ClassLoader {
private String path;
protected MyClassLoader(String path) {
this.path = path;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class aClass = null;
// 獲取二進制字節流
byte[] classBytes = loadClassBytes(name);
if (classBytes == null) {
System.out.println("class data is null");
} else {
aClass = defineClass(name, classBytes, 0, classBytes.length);
}
return aClass;
}
private byte[] loadClassBytes(String name) {
String fileName = getFileName(name);
File file = new File(path, fileName);
InputStream inputStream = null;
ByteArrayOutputStream outputStream = null;
try {
inputStream = new FileInputStream(file);
outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
return outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private String getFileName(String name) {
String fileName;
int index = name.lastIndexOf(".");
if (index == -1) {
fileName = name + ".class";
} else {
fileName = name.substring(index + 1) + ".class";
}
return fileName;
}
}
複製代碼
自定義類加載器分爲如下幾個步驟:
ClassLoader
。findClass
方法。findClass
方法中獲取到二進制字節流後,調用defineClass
方法。在MyClassLoader
的findClass
方法中首先讀取到本地硬盤下的TestPrint.class
文件的字節流,而後調用defineClass
方法,該方法會將字節流轉化爲Class
類型。最後寫一個測試類調用。
public class TestMyClassLoader {
public static void main(String[] args) {
// 初始化類加載器
MyClassLoader myClassLoader = new MyClassLoader("/Users/sy/Downloads/ClassLoader");
try {
// 使用自定義類加載器獲取Class
Class<?> printTest = myClassLoader.loadClass("TestPrint");
// 建立實例
Object instance = printTest.newInstance();
System.out.println("classloader:" + instance.getClass().getClassLoader());
Method method = printTest.getDeclaredMethod("printString", null);
method.setAccessible(true);
method.invoke(instance, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製代碼
運行結果:
Base64
加密,須要在
defineClass
前進行一個解碼操做,修改
findClass
方法。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class aClass = null;
// 獲取二進制字節流
byte[] classBytes = loadClassBytes(name);
// Base64 decode
classBytes = Base64.getDecoder().decode(classBytes);
if (classBytes == null) {
System.out.println("class data is null");
} else {
aClass = defineClass(name, classBytes, 0, classBytes.length);
}
return aClass;
}
複製代碼
再次運行查看結果:
學習類加載器就避免不了要了解雙親委託,它是類加載器尋找加載類的模式。仍是先看到以前自定義類加載器的例子,自定義時複寫了findClass
方法,可是使用時卻沒有直接調用這個方法,使用時是經過ClassLoader.loadClass
方法得到Class
的。雙親委託模式就是在這個方法中實現的,因而進入查看源碼。
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先檢查該類是否加載過
Class<?> c = findLoadedClass(name);
// c爲空說明沒有加載過
if (c == null) {
long t0 = System.nanoTime();
try {
// 判斷是否有父類加載器
if (parent != null) {
// 有父類加載器就調用父類加載器的loadClass方法
c = parent.loadClass(name, false);
} else {
// 沒有父類加載器就調用這個方法
// 方法中會調用native方法findBootstrapClass使用BootstrapClassLoader檢查該類是否已加載
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 走到此處c還爲null說明父類加載器沒有加載該類
if (c == null) {
long t1 = System.nanoTime();
// 就調用自身的findClass查找加載該類
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
複製代碼
loadClass
中的代碼邏輯很是清晰的描述了雙親委託模式,首先經過findLoadedClass
方法檢驗要加載的類是否被加載過。加載過直接返回該類的Class
對象,沒加載過則c
爲空進入下面的判斷,判斷父類加載器parent
是否爲空,不爲空調用父類的loadClass
方法,爲空則調用findBootstrapClassOrNull
方法,該方法中會繼續調用native
方法findBootstrapClass
使用BootstrapClassLoader
檢查該類是否已加載。接着若是沒有加載該類就會調用自身的findClass
方法查找加載該類。
最後對雙親委託模式作個總結:雙親委託模式是指類加載器加載一個類首先是判斷這個類是否加載過,沒加載過不會本身直接加載,而是委託給其父類加載器去查找,一直委託到頂層引導類加載器BootstrapClassLoader
,若是BootstrapClassLoader
找到該Class
就會直接返回,沒找到就會交給子類加載器依次向下查找,一直沒找到最後就會交給自身去查找。
雙親委託模式有兩個好處:
Class
。java.lang.Object
類不管使用哪一個類加載器加載,最終都會委託給頂層BootstrapClassLoader
來加載,這樣保證了Object
類永遠是同一個類,不會出現多個不一樣的Object
類。這樣也沒法經過自定義一個Object
類替換系統原來的Object
類。另外Java
虛擬機判斷兩個類是同一個類,是依據兩個類類名一致,而且被同一個類加載器加載。這裏能夠在以前的自定義類加載器的基礎上測試下。將原來的MyClassLoader
複製後重命名YourClassLoader
一份,再編寫一個測試類。
public class TestDifferentClassLoader {
public static void main(String[] args) {
MyClassLoader myClassLoader = new MyClassLoader("/Users/sy/Downloads/ClassLoader/");
YourClassLoader yourClassLoader = new YourClassLoader("/Users/sy/Downloads/ClassLoader/");
try {
Class<?> myPrintTest = myClassLoader.loadClass("TestPrint");
// 使用不一樣的類加載器
Class<?> yourPrintTest = yourClassLoader.loadClass("TestPrint");
Object myInstance = myPrintTest.newInstance();
Object yourInstance = yourPrintTest.newInstance();
System.out.println(myInstance.getClass().equals(yourInstance.getClass()));
System.out.println(myInstance.getClass().getName());
System.out.println(yourInstance.getClass().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製代碼
運行結果:
public class TestDifferentClassLoader {
public static void main(String[] args) {
MyClassLoader myClassLoader = new MyClassLoader("D:\\");
YourClassLoader yourClassLoader = new YourClassLoader("D:\\");
try {
Class<?> myPrintTest = myClassLoader.loadClass("TestPrint");
// 用同一個類加載器
Class<?> yourPrintTest = myClassLoader.loadClass("TestPrint");
Object myInstance = myPrintTest.newInstance();
Object yourInstance = yourPrintTest.newInstance();
System.out.println(myInstance.getClass().equals(yourInstance.getClass()));
System.out.println(myInstance.getClass().getName());
System.out.println(yourInstance.getClass().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製代碼
運行結果:
Android
中類加載器與Java
中的類加載器相似可是並不徹底相同。Java
中類加載器是加載.class
文件,而Android
中是加載的dex
文件,不過Android
中也分系統類加載器和自定義類加載器,系統類加載器主要包括如下三種:BootClassLoader
、DexClassLoader
和PathClassLoader
。這裏仍是先循環打印一下Android
中的父類加載器。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClassLoader classLoader = MainActivity.class.getClassLoader();
while (classLoader != null) {
Log.d("classLoader", "name:" + classLoader);
classLoader = classLoader.getParent();
}
}
}
複製代碼
運行日誌結果:
從日誌能夠看到這裏有兩個類加載器,一個是BootClassLoader
另外一個是PathClassLoader
。Android
中的每一個類加載器裏一樣保存一個父類加載器的引用,getParent
方法獲取到的一樣是這個ClassLoader
,仍是先來看一下Android
中的ClassLoader
類。
public abstract class ClassLoader {
private final ClassLoader parent;
......
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 檢查該類是否已經加載
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
// 父類加載器不爲空則調用其的loadClass方法
c = parent.loadClass(name, false);
} else {
// 父類加載器爲空就調用findBootstrapClassOrNull方法
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 父類加載器沒有查找到該類,則調用自身findClass方法查找加載
c = findClass(name);
}
}
return c;
}
private Class<?> findBootstrapClassOrNull(String name)
{
return null;
}
public final ClassLoader getParent() {
return parent;
}
......
}
複製代碼
Android
中的ClassLoader
類一樣是一個抽象類,它的loadClass
方法中的邏輯和Java
中的相似,一樣是遵循了雙親委託模式,父類加載器不爲空則調用父類加載器的loadClass
方法,爲空則調用findBootstrapClassOrNull
方法,該方法這裏直接返回的null
。若父類加載器沒有查找到須要加載的類,則調用自身的findClass
方法。
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
複製代碼
接着看到ClassLoader
中的findClass
裏是直接拋出了一個ClassNotFoundException
異常,說明這個方法須要子類來實現。那麼接下來就來看看Android
中類加載器的實際繼承關係。
抽象類ClassLoader
定義了類加載器的主要功能,它的子類如上圖。先根據源碼梳理下它們之間的關係。
public class BaseDexClassLoader extends ClassLoader {
......
}
public class SecureClassLoader extends ClassLoader {
......
}
public class DexClassLoader extends BaseDexClassLoader {
......
}
public class PathClassLoader extends BaseDexClassLoader {
......
}
public final class InMemoryDexClassLoader extends BaseDexClassLoader {
......
}
public class URLClassLoader extends SecureClassLoader implements Closeable {
......
}
public final class DelegateLastClassLoader extends PathClassLoader {
......
}
複製代碼
除了這些子類,抽象類ClassLoader
中還有一個內部類BootClassLoader
。
class BootClassLoader extends ClassLoader {
......
}
複製代碼
綜上所述Android
中類加載器的繼承關係以下圖。
接下來簡單瞭解一下這些類加載器。
class BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
@FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
}
return instance;
}
public BootClassLoader() {
super(null);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return Class.classForName(name, false, null);
}
......
@Override
protected Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
clazz = findClass(className);
}
return clazz;
}
......
}
複製代碼
BootClassLoader
是ClassLoader
的內部類繼承自ClassLoader
,而且提供了一個獲取單例的getInstance
方法。與Java
中不一樣BootClassLoader
不是用C/C++
實現的是用Java
實現的,Android
系統啓動時會使用BootClassLoader
來預加載經常使用類。
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
複製代碼
DexClassLoader
看名字就知道是用來加載dex
文件的,它的構造函數中有四個參數:
dex
相關文件路徑集合。dex
文件存儲路徑。C/C++
庫的路徑集合。DexClassLoader
繼承自BaseDexClassLoader
其主要的方法都在其父類中實現。
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
複製代碼
PathClassLoader
是用來加載系統類和應用程序的類,一樣繼承自BaseDexClassLoader
類, 它的構造函數裏沒有optimizedDirectory
參數,一般用來加載已經安裝的dex
文件。
BaseDexClassLoader
是DexClassLoader
和PathClassLoader
的父類。
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
......
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
if (reporter != null) {
reporter.report(this.pathList.getDexPaths());
}
}
......
@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);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
.......
}
複製代碼
在它的構造方法中先是建立了一個DexPathList
對象,接着它的findClass
方法中是經過pathList.findClass
方法得到的Class
對象。
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
複製代碼
findClass
方法中循環遍歷了dexElements
數組,再經過Element.findClass
方法來得到Class
對象,Element
又是DexPathList
的內部類,進一步找到它的findClass
方法查看。
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
複製代碼
這個方法中又調用了DexFile
的loadClassBinaryName
方法。
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List<Throwable> suppressed) {
Class result = null;
try {
result = defineClassNative(name, loader, cookie, dexFile);
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
複製代碼
loadClassBinaryName
方法裏繼續調用了defineClass
方法,而defineClass
方法裏最終調用了defineClassNative
這個native
方法加載dex
文件。
Android
系統啓動後init
進程會啓動Zygote
進程,Zygote
進程初始化時會預加載經常使用類,進而會調用Class.forName
方法,該方法裏會經過BootClassLoader.getInstance
方法建立BootClassLoader
。以後Zygote
進程又會啓動SystemServer
進程,進而又會經過PathClassLoaderFactory
建立PathClassLoader
。總而言之,Android
中的這兩個類加載器在啓動時就進行初始化建立了。
Java
中類加載生命週期包括:加載、鏈接、初始化、使用、卸載這些階段,其中鏈接階段又分爲驗證、準備、解析。Java
中的 系統類加載器與Android
中的系統類加載器並不相同,但都遵循了雙親委託模式。ClassLoader
類複寫它的findClass
方法實現本身的類加載器。