前言
在上一篇文章我們學(xué)習(xí)了Java的ClassLoader,很多同學(xué)會把Java和Android的ClassLoader搞混狂丝,甚至?xí)J為Android中的ClassLoader和Java中的ClassLoader是一樣的具被,這顯然是不對的豁翎。這一篇文章我們就來學(xué)習(xí)Android中的ClassLoader,來看看它和Java中的ClassLoader有何不同。
1.ClassLoader的類型
我們知道Java中的ClassLoader可以加載jar文件和Class文件(本質(zhì)是加載Class文件)秉颗,這一點在Android中并不適用稽穆,因為無論是DVM還是ART它們加載的不再是Class文件,而是dex文件米丘,這就需要重新設(shè)計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)啟動時會使用BootClassLoader來預(yù)加載常用類梭依,與Java中的BootClassLoader不同,它并不是由C/C++代碼實現(xiàn)典尾,而是由Java實現(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是一個單例類,需要注意的是BootClassLoader的訪問修飾符是默認的勃教,只有在同一個包中才可以訪問淤击,因此我們在應(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)造方法有四個參數(shù):
- dexPath:dex相關(guān)文件路徑集合射赛,多個路徑用文件分隔符分隔,默認文件分隔符為‘:’
- optimizedDirectory:解壓的dex文件存儲路徑奶是,這個路徑必須是一個內(nèi)部存儲路徑楣责,一般情況下使用當(dāng)前應(yīng)用程序的私有路徑:
/data/data/<Package Name>/...
。 - librarySearchPath:包含 C/C++ 庫的路徑集合聂沙,多個路徑用文件分隔符分隔分割秆麸,可以為null。
- parent:父加載器及汉。
DexClassLoader 繼承自BaseDexClassLoader 沮趣,方法實現(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房铭,實現(xiàn)也都在BaseDexClassLoader中。
PathClassLoader的構(gòu)造方法中沒有參數(shù)optimizedDirectory温眉,這是因為PathClassLoader已經(jīng)默認了參數(shù)optimizedDirectory的值為:/data/dalvik-cache育叁,很顯然PathClassLoader無法定義解壓的dex文件存儲路徑,因此PathClassLoader通常用來加載已經(jīng)安裝的apk的dex文件(安裝的apk的dex文件會存儲在/data/dalvik-cache中)芍殖。
2.ClassLoader的繼承關(guān)系
運行一個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)用安裝在手機上的位置洒琢。關(guān)于DexPathList后續(xù)文章會進行介紹秧秉。
和Java中的ClassLoader一樣,雖然系統(tǒng)所提供的類加載器有3種類型衰抑,但是系統(tǒng)提供的ClassLoader相關(guān)類卻不只3個象迎。ClassLoader的繼承關(guān)系如下圖所示。
可以看到上面一共有7個ClassLoader相關(guān)類呛踊,其中有一些和Java中的ClassLoader相關(guān)類十分類似砾淌,下面簡單對它們進行介紹:
- ClassLoader是一個抽象類,其中定義了ClassLoader的主要功能谭网。BootClassLoader是它的內(nèi)部類汪厨。
- SecureClassLoader類和JDK8中的SecureClassLoader類的代碼是一樣的,它繼承了抽象類ClassLoader愉择。SecureClassLoader并不是ClassLoader的實現(xiàn)類劫乱,而是拓展了ClassLoader類加入了權(quán)限方面的功能,加強了ClassLoader的安全性薄辅。
- URLClassLoader類和JDK8中的URLClassLoader類的代碼是一樣的要拂,它繼承自SecureClassLoader,用來通過URl路徑從jar文件和文件夾中加載類和資源站楚。
- BaseDexClassLoader繼承自ClassLoader脱惰,是抽象類ClassLoader的具體實現(xiàn)類,PathClassLoader和DexClassLoader都繼承它窿春。
3.BootClassLoader的創(chuàng)建
BootClassLoader是在何時被創(chuàng)建的呢拉一?這得先從Zygote進程開始說起,不了解Zygote進程的可以查看Android系統(tǒng)啟動流程(二)解析Zygote進程這篇文章旧乞。
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進程初始化時預(yù)加載常用類。注釋1處將/system/etc/preloaded-classes文件封裝成FileInputStream除盏,preloaded-classes文件中存有預(yù)加載類的目錄叉橱,這個文件在系統(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ù)加載屬于拿空間換時間的策略踱侣,Zygote環(huán)境配置的越健全越通用粪小,應(yīng)用程序進程需要單獨做的事情也就越少,預(yù)加載除了預(yù)加載類抡句,還有預(yù)加載資源和預(yù)加載共享庫探膊,因為不是本文重點,這里就不在延伸講下去了玉转。
回到preloadClasses方法的注釋2處突想,將FileInputStream封裝為BufferedReader,并注釋3處遍歷BufferedReader究抓,讀出所有預(yù)加載類的名稱猾担,每讀出一個預(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實例傳入到了注釋2處的classForName方法中,classForName方法是Native方法橘茉,它的實現(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進程開始說起畅卓,Zygote進程啟動SyetemServer進程時會調(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進程通過forkSystemServer方法fork自身創(chuàng)建子進程(SystemServer進程)趁冈。注釋2處如果forkSystemServer方法返回的pid等于0,說明當(dāng)前代碼是在新創(chuàng)建的SystemServer進程中執(zhí)行的拜马,接著就會執(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方法中又會調(diào)用PathClassLoaderFactory的createClassLoader方法俩莽,看來PathClassLoader是用工廠來進行創(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方法中會創(chuàng)建PathClassLoader。
結(jié)語
在這篇文章中我們學(xué)習(xí)了Android的ClassLoader的類型扮超、ClassLoader的繼承關(guān)系以及BootClassLoader和PathClassLoader是何時創(chuàng)建的取刃。BootClassLoader是在Zygote進程的入口方法中創(chuàng)建的蹋肮,PathClassLoader則是在Zygote進程創(chuàng)建SystemServer進程時創(chuàng)建的。