相關文章
Java虛擬機系列
Android系統啓動系列
Android解析ClassLoader系列html
在上一篇文章咱們學習了Java的ClassLoader,不少同窗會把Java和Android的ClassLoader搞混,甚至會認爲Android中的ClassLoader和Java中的ClassLoader是同樣的,這顯然是不對的。這一篇文章咱們就來學習Android中的ClassLoader,來看看它和Java中的ClassLoader有何不一樣。
java
咱們知道Java中的ClassLoader能夠加載jar文件和Class文件(本質是加載Class文件),這一點在Android中並不適用,由於不管是DVM仍是ART它們加載的再也不是Class文件,而是dex文件,這就須要從新設計ClassLoader相關類,咱們先來學習ClassLoader的類型。
Android中的ClassLoader類型和Java中的ClassLoader類型相似,也分爲兩種類型,分別是系統ClassLoader和自定義ClassLoader。其中系統ClassLoader包括三種分別是BootClassLoader、PathClassLoader和DexClassLoader。android
Android系統啓動時會使用BootClassLoader來預加載經常使用類,與Java中的BootClassLoader不一樣,它並非由C/C++代碼實現,而是由Java實現的,BootClassLoade的代碼以下所示。
libcore/ojluni/src/main/java/java/lang/ClassLoader.javac++
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的內部類,並繼承自ClassLoader。BootClassLoader是一個單例類,須要注意的是BootClassLoader的訪問修飾符是默認的,只有在同一個包中才能夠訪問,所以咱們在應用程序中是沒法直接調用的。安全
Android系統使用PathClassLoader來加載系統類和應用程序的類,若是是加載非系統應用程序類,則會加載data/app/目錄下的dex文件以及包含dex的apk文件或jar文件,不論是加載哪一種文件,最終都是要加載dex文件,在這裏爲了方便理解,咱們將dex文件以及包含dex的apk文件或jar文件統稱爲dex相關文件。PathClassLoader不建議開發直接使用。來查看它的代碼:
libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.javabash
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,很明顯PathClassLoader的方法實現都在BaseDexClassLoader中。從PathClassLoader的構造方法也能夠看出它遵循了雙親委託模式,不瞭解雙親委託模式請查看 Android解析ClassLoader(一)Java中的ClassLoader 這篇文章。
PathClassLoader的構造方法有三個參數:app
DexClassLoader能夠加載dex文件以及包含dex的apk文件或jar文件,也支持從SD卡進行加載,這也就意味着DexClassLoader能夠在應用未安裝的狀況下加載dex相關文件。所以,它是熱修復和插件化技術的基礎。來查看它的代碼,以下所示。
libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.javasocket
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}
}複製代碼
DexClassLoader構造方法的參數要比PathClassLoader多一個optimizedDirectory參數,參數optimizedDirectory表明什麼呢?咱們知道應用程序第一次被加載的時候,爲了提升之後的啓動速度和執行效率,Android系統會對dex相關文件作必定程度的優化,並生成一個ODEX文件,此後再運行這個應用程序的時候,只要加載優化過的ODEX文件就好了,省去了每次都要優化的時間,而參數optimizedDirectory就是表明存儲ODEX文件的路徑,這個路徑必須是一個內部存儲路徑。
PathClassLoader沒有參數optimizedDirectory,這是由於PathClassLoader已經默認了參數optimizedDirectory的路徑爲:/data/dalvik-cache
。DexClassLoader 也繼承自BaseDexClassLoader ,方法實現也都在BaseDexClassLoader中。ide
運行一個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打印出來,接着打印出當前類的類加載器的父加載器,直到沒有父加載器終止循環。打印結果以下所示。
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就是示例應用安裝在手機上的位置。關於DexPathList後續文章會進行介紹。
和Java中的ClassLoader同樣,雖然系統所提供的類加載器有3種類型,可是系統提供的ClassLoader相關類卻不僅3個。ClassLoader的繼承關係以下圖所示。
BootClassLoader是在什麼時候被建立的呢?這得先從Zygote進程開始提及,不瞭解Zygote進程的能夠查看Android系統啓動流程(二)解析Zygote進程啓動過程這篇文章。
ZygoteInit的main方法以下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
...
try {
...
preload(bootTimingsTraceLog);
...
}
}複製代碼
main方法是ZygoteInit入口方法,其中調用了ZygoteInit的preload方法,preload方法中又調用了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進程初始化時預加載經常使用類。註釋1處將/system/etc/preloaded-classes文件封裝成FileInputStream,preloaded-classes文件中存有預加載類的目錄,這個文件在系統源碼中的路徑爲frameworks/base/preloaded-classes,這裏列舉一些preloaded-classes文件中的預加載類名稱,以下所示。
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文件中的預加載類的名稱有不少都是咱們很是熟知的。預加載屬於拿空間換時間的策略,Zygote環境配置的越健全越通用,應用程序進程須要單獨作的事情也就越少,預加載除了預加載類,還有預加載資源和預加載共享庫,由於不是本文重點,這裏就不在延伸講下去了。
回到preloadClasses方法的註釋2處,將FileInputStream封裝爲BufferedReader,並註釋3處遍歷BufferedReader,讀出全部預加載類的名稱,每讀出一個預加載類的名稱就調用註釋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處建立了BootClassLoader,並將BootClassLoader實例傳入到了註釋2處的classForName方法中,classForName方法是Native方法,它的實現由c/c++代碼來完成,以下所示。
@FastNative
static native Class<?> classForName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException;複製代碼
PathClassLoader的建立也得從Zygote進程開始提及,Zygote進程啓動SyetemServer進程時會調用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自身建立子進程(SystemServer進程)。註釋2處若是forkSystemServer方法返回的pid等於0,說明當前代碼是在新建立的SystemServer進程中執行的,接着就會執行註釋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處調用了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方法中又會調用PathClassLoaderFactory的createClassLoader方法,看來PathClassLoader是用工廠來進行建立的。
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方法中會建立PathClassLoader。
在這篇文章中咱們學習了Android的ClassLoader的類型、ClassLoader的繼承關係以及BootClassLoader和PathClassLoader是什麼時候建立的。BootClassLoader是在Zygote進程的入口方法中建立的,PathClassLoader則是在Zygote進程建立SystemServer進程時建立的。本系列後續文章會接着介紹Android中的ClassLoader的其餘知識點,敬請期待。
參考資料
Android動態加載之ClassLoader詳解
熱修復入門:Android 中的 ClassLoader
淺析dex文件加載機制