前言
在上一篇文章我們學(xué)習(xí)了Java的ClassLoader,很多同學(xué)會(huì)把Java和Android的ClassLoader搞混,甚至?xí)J(rèn)為Android中的ClassLoader和Java中的ClassLoader是一樣的,這顯然是不對(duì)的。這一篇文章我們就來學(xué)習(xí)Android中的ClassLoader,來看看它和Java中的ClassLoader有何不同。
1.ClassLoader的類型
我們知道Java中的ClassLoader可以加載jar文件和Class文件(本質(zhì)是加載Class文件),這一點(diǎn)在Android中并不適用,因?yàn)闊o論是DVM還是ART它們加載的不再是Class文件,而是dex文件,這就需要重新設(shè)計(jì)ClassLoader相關(guān)類,我們先來學(xué)習(xí)ClassLoader的類型。
Android中的ClassLoader類型和Java中的ClassLoader類型類似,也分為兩種類型,分別是系統(tǒng)ClassLoader和自定義ClassLoader。其中系統(tǒng)ClassLoader包括三種分別是BootClassLoader、PathClassLoader和DexClassLoader。
1.1 BootClassLoader
Android系統(tǒng)啟動(dòng)時(shí)會(huì)使用BootClassLoader來預(yù)加載常用類,與Java中的BootClassLoader不同,它并不是由C/C++代碼實(shí)現(xiàn),而是由Java實(shí)現(xiàn)的,BootClassLoade的代碼如下所示。
libcore/ojluni/src/main/java/java/lang/ClassLoader.java
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;
}
...
}
BootClassLoader是ClassLoader的內(nèi)部類,并繼承自ClassLoader。BootClassLoader是一個(gè)單例類,需要注意的是BootClassLoader的訪問修飾符是默認(rèn)的,只有在同一個(gè)包中才可以訪問,因此我們?cè)趹?yīng)用程序中是無法直接調(diào)用的。
1.2 DexClassLoader
DexClassLoader可以加載dex文件以及包含dex的壓縮文件(apk和jar文件),不管是加載哪種文件,最終都是要加載dex文件,為了方便理解和敘述,將dex文件以及包含dex的壓縮文件統(tǒng)稱為dex相關(guān)文件。
來查看DexClassLoader的代碼,如下所示。
libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}
DexClassLoader的構(gòu)造方法有四個(gè)參數(shù):
- dexPath:dex相關(guān)文件路徑集合,多個(gè)路徑用文件分隔符分隔,默認(rèn)文件分隔符為‘:’
- optimizedDirectory:解壓的dex文件存儲(chǔ)路徑,這個(gè)路徑必須是一個(gè)內(nèi)部存儲(chǔ)路徑,一般情況下使用當(dāng)前應(yīng)用程序的私有路徑:
/data/data/<Package Name>/...。 - librarySearchPath:包含 C/C++ 庫的路徑集合,多個(gè)路徑用文件分隔符分隔分割,可以為null。
- parent:父加載器。
DexClassLoader 繼承自BaseDexClassLoader ,方法實(shí)現(xiàn)都在BaseDexClassLoader中。
1.3 PathClassLoader
Android系統(tǒng)使用PathClassLoader來加載系統(tǒng)類和應(yīng)用程序的類,來查看它的代碼:
libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
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,實(shí)現(xiàn)也都在BaseDexClassLoader中。
PathClassLoader的構(gòu)造方法中沒有參數(shù)optimizedDirectory,這是因?yàn)镻athClassLoader已經(jīng)默認(rèn)了參數(shù)optimizedDirectory的值為:/data/dalvik-cache,很顯然PathClassLoader無法定義解壓的dex文件存儲(chǔ)路徑,因此PathClassLoader通常用來加載已經(jīng)安裝的apk的dex文件(安裝的apk的dex文件會(huì)存儲(chǔ)在/data/dalvik-cache中)。
2.ClassLoader的繼承關(guān)系
運(yùn)行一個(gè)Android程序需要用到幾種類型的類加載器呢?如下所示。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClassLoader loader = MainActivity.class.getClassLoader();
while (loader != null) {
Log.d("liuwangshu",loader.toString());//1
loader = loader.getParent();
}
}
}
首先我們得到MainActivity的類加載器,并在注釋1處通過Log打印出來,接著打印出當(dāng)前類的類加載器的父加載器,直到?jīng)]有父加載器終止循環(huán)。打印結(jié)果如下所示。
10-07 07:23:02.835 8272-8272/? D/liuwangshu: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.liuwangshu.moonclassloader-2/base.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.liuwangshu.moonclassloader-2/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.example.liuwangshu.moonclassloader-2/lib/x86, /vendor/lib, /system/lib]]]
10-07 07:23:02.835 8272-8272/? D/liuwangshu: java.lang.BootClassLoader@e175998
可以看到有兩種類加載器,一種是PathClassLoader,另一種則是BootClassLoader。DexPathList中包含了很多apk的路徑,其中/data/app/com.example.liuwangshu.moonclassloader-2/base.apk就是示例應(yīng)用安裝在手機(jī)上的位置。關(guān)于DexPathList后續(xù)文章會(huì)進(jìn)行介紹。
和Java中的ClassLoader一樣,雖然系統(tǒng)所提供的類加載器有3種類型,但是系統(tǒng)提供的ClassLoader相關(guān)類卻不只3個(gè)。ClassLoader的繼承關(guān)系如下圖所示。

可以看到上面一共有7個(gè)ClassLoader相關(guān)類,其中有一些和Java中的ClassLoader相關(guān)類十分類似,下面簡(jiǎn)單對(duì)它們進(jìn)行介紹:
- ClassLoader是一個(gè)抽象類,其中定義了ClassLoader的主要功能。BootClassLoader是它的內(nèi)部類。
- SecureClassLoader類和JDK8中的SecureClassLoader類的代碼是一樣的,它繼承了抽象類ClassLoader。SecureClassLoader并不是ClassLoader的實(shí)現(xiàn)類,而是拓展了ClassLoader類加入了權(quán)限方面的功能,加強(qiáng)了ClassLoader的安全性。
- URLClassLoader類和JDK8中的URLClassLoader類的代碼是一樣的,它繼承自SecureClassLoader,用來通過URl路徑從jar文件和文件夾中加載類和資源。
- BaseDexClassLoader繼承自ClassLoader,是抽象類ClassLoader的具體實(shí)現(xiàn)類,PathClassLoader和DexClassLoader都繼承它。
3.BootClassLoader的創(chuàng)建
BootClassLoader是在何時(shí)被創(chuàng)建的呢?這得先從Zygote進(jìn)程開始說起,不了解Zygote進(jìn)程的可以查看Android系統(tǒng)啟動(dòng)流程(二)解析Zygote進(jìn)程這篇文章。
ZygoteInit的main方法如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
...
try {
...
preload(bootTimingsTraceLog);
...
}
}
main方法是ZygoteInit入口方法,其中調(diào)用了ZygoteInit的preload方法,preload方法中又調(diào)用了ZygoteInit的preloadClasses方法,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);//1
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
...
try {
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);//2
int count = 0;
String line;
while ((line = br.readLine()) != null) {//3
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line, true, null);//4
count++;
} catch (ClassNotFoundException e) {
Log.w(TAG, "Class not found for preloading: " + line);
}
...
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
...
}
}
preloadClasses方法用于Zygote進(jìn)程初始化時(shí)預(yù)加載常用類。注釋1處將/system/etc/preloaded-classes文件封裝成FileInputStream,preloaded-classes文件中存有預(yù)加載類的目錄,這個(gè)文件在系統(tǒng)源碼中的路徑為frameworks/base/preloaded-classes,這里列舉一些preloaded-classes文件中的預(yù)加載類名稱,如下所示。
android.app.ApplicationLoaders
android.app.ApplicationPackageManager
android.app.ApplicationPackageManager$OnPermissionsChangeListenerDelegate
android.app.ApplicationPackageManager$ResourceName
android.app.ContentProviderHolder
android.app.ContentProviderHolder$1
android.app.ContextImpl
android.app.ContextImpl$ApplicationContentResolver
android.app.DexLoadReporter
android.app.Dialog
android.app.Dialog$ListenersHandler
android.app.DownloadManager
android.app.Fragment
可以看到preloaded-classes文件中的預(yù)加載類的名稱有很多都是我們非常熟知的。預(yù)加載屬于拿空間換時(shí)間的策略,Zygote環(huán)境配置的越健全越通用,應(yīng)用程序進(jìn)程需要單獨(dú)做的事情也就越少,預(yù)加載除了預(yù)加載類,還有預(yù)加載資源和預(yù)加載共享庫,因?yàn)椴皇潜疚闹攸c(diǎn),這里就不在延伸講下去了。
回到preloadClasses方法的注釋2處,將FileInputStream封裝為BufferedReader,并注釋3處遍歷BufferedReader,讀出所有預(yù)加載類的名稱,每讀出一個(gè)預(yù)加載類的名稱就調(diào)用注釋4處的代碼加載該類,Class的forName方法如下所示。
libcore/ojluni/src/main/java/java/lang/Class.java
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
loader = BootClassLoader.getInstance();//1
}
Class<?> result;
try {
result = classForName(name, initialize, loader);//2
} catch (ClassNotFoundException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
throw e;
}
return result;
}
注釋1處創(chuàng)建了BootClassLoader,并將BootClassLoader實(shí)例傳入到了注釋2處的classForName方法中,classForName方法是Native方法,它的實(shí)現(xiàn)由c/c++代碼來完成,如下所示。
@FastNative
static native Class<?> classForName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException;
4.PathClassLoader的創(chuàng)建
PathClassLoader的創(chuàng)建也得從Zygote進(jìn)程開始說起,Zygote進(jìn)程啟動(dòng)SyetemServer進(jìn)程時(shí)會(huì)調(diào)用ZygoteInit的startSystemServer方法,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException {
...
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);//2
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/*1*/
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
if (pid == 0) {//2
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);//3
}
return true;
}
注釋1處,Zygote進(jìn)程通過forkSystemServer方法fork自身創(chuàng)建子進(jìn)程(SystemServer進(jìn)程)。注釋2處如果forkSystemServer方法返回的pid等于0,說明當(dāng)前代碼是在新創(chuàng)建的SystemServer進(jìn)程中執(zhí)行的,接著就會(huì)執(zhí)行注釋3處的handleSystemServerProcess方法:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws Zygote.MethodAndArgsCaller {
...
if (parsedArgs.invokeWith != null) {
...
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);//1
Thread.currentThread().setContextClassLoader(cl);
}
ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
}
注釋1處調(diào)用了createPathClassLoader方法,如下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
return PathClassLoaderFactory.createClassLoader(classPath,
libraryPath,
libraryPath,
ClassLoader.getSystemClassLoader(),
targetSdkVersion,
true /* isNamespaceShared */);
}
createPathClassLoader方法中又會(huì)調(diào)用PathClassLoaderFactory的createClassLoader方法,看來PathClassLoader是用工廠來進(jìn)行創(chuàng)建的。
frameworks/base/core/java/com/android/internal/os/PathClassLoaderFactory.java
public static PathClassLoader createClassLoader(String dexPath,
String librarySearchPath,
String libraryPermittedPath,
ClassLoader parent,
int targetSdkVersion,
boolean isNamespaceShared) {
PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);
...
return pathClassloader;
}
在PathClassLoaderFactory的createClassLoader方法中會(huì)創(chuàng)建PathClassLoader。
結(jié)語
在這篇文章中我們學(xué)習(xí)了Android的ClassLoader的類型、ClassLoader的繼承關(guān)系以及BootClassLoader和PathClassLoader是何時(shí)創(chuàng)建的。BootClassLoader是在Zygote進(jìn)程的入口方法中創(chuàng)建的,PathClassLoader則是在Zygote進(jìn)程創(chuàng)建SystemServer進(jìn)程時(shí)創(chuàng)建的。