上一篇文章咱們瞭解了Java
的ClassLoader
,上一篇文章傳送門JVM 類加載機制java
其實Android
中的ClassLoader
和java
中的是不同的,由於java
中的CalssLoader
主要加載Class
文件,可是Android
中的ClassLoader
主要加載dex
文件數組
Android
中的ClassLoader
分爲倆種類型,系統類加載器
,自定義類加載器
。其中系統的類加載器
分爲三種,BootClassLoader
,PathClassLoader
,DexClassLoader
安全
Android 系統啓動時,會用BootClassLoader來預加載經常使用類,與java中的ClassLoader不一樣,他不是用C++實現,而是用java實現的,以下:cookie
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
是默認修飾符,只能包內訪問,咱們是沒法使用的app
DexClassLoader能夠加載dex文件和包含dex文件的壓縮文件(好比jar和apk文件),無論加載那種文件,最終都是加載dex文件,咱們看一下代碼ide
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
複製代碼
DexClassLoader有四個參數post
/data/data/<Package Name>/...
DexClassLoade
繼承自BaseDexClassLoader
,全部的實現都是在BaseDexClassLoader
中this
Android
用PathClassLoader
來加載系統類和應用程序類,代碼以下:spa
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
複製代碼
PathClassLoader
繼承自BaseDexClassLoader
,全部的實現都是在BaseDexClassLoader
中code
PathClassLoader
構造方法沒有optimizedDirectory
參數,由於PathClassLoader
默認optimizedDirectory
參數是/data/dalvik-cache
,很顯然PathClassLoader
沒法定義解壓的dex
儲存的位置,所以PathClassLoader
一般用來加載已經安裝的apk
的dex
文件(安裝的apk的dex
文件在/data/dalvik-cache
中)
運行一個應用程序須要幾個ClassLoader呢?
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("mmmClassLoader", classLoader.toString()+"\n");
classLoader = classLoader.getParent();
}
}
複製代碼
看下log
mmmClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.baidu.bpit.aibaidu.idl3-2/base.apk"],
nativeLibraryDirectories=[/data/app/com.baidu.bpit.aibaidu.idl3-2/lib/arm64, /data/app/com.baidu.bpit.aibaidu.idl3-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]]
java.lang.BootClassLoader@fcb14c9
複製代碼
咱們看到主要用了倆個ClassLoader
,分別是PathClassLoader
和BootClassLoader
,其中DexPathList
包含了不少apk
的路徑,其中/data/app/com.baidu.bpit.aibaidu.idl3-2/base.apk
就是實例應用安裝在手機上的位置,DexPathList
是在BaseDexClassLoder
中建立的,裏面儲存dex
相關文件的路徑
除了上方的3中ClassLoader,Android還提供了其餘的類加載器和ClassLoader相關類,繼承關係以下:
分別介紹一下上方的8種ClassLoader
ClassLoader
是一個抽象類,其中定義了ClassLoder
的主要功能,BootClassLoader
是他的內部類SecureClassLoader
和JDK8中的SecureClassLoader
代碼是同樣的,他繼承抽象類ClassLoader
,SecureClassLoader
並非ClassLoader
的實現類,而是擴展了ClassLoader
權限方面的功能,增強了Classloader
的安全性URLClassLoader
和JDK8中的URLClassLoader是同樣的,他繼承了SecureClassLoader
通能過URL
路徑從jar文件中加載類和資源InMemoryDexClassLoader
他是Android8.0新加的類加載器,繼承自BaseDexClassLoader
,用於加載內存中的dex文件BaseDexClassLoader
繼承自ClassLoader
,是抽象類ClassLoader
的具體實現類Android
中的ClassLoader
一樣遵循雙親委託模型,ClassLoader
的加載方法爲loadClass
,方法定義在ClassLoader中
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException{
// 首先檢查類是否被加載過
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//若是父類拋出ClassNotFoundException異常
//則說明父類不能加載該類
}
if (c == null) {
//若是父類沒法加載,則調用自身的findClass進行加
c = findClass(name);
}
}
return c;
}
複製代碼
上方邏輯很清楚,首先檢查類是否被加載過,若是沒有被加載過,就調用父類的加載器加載,若是父類加載器爲空就調用啓動加載器加載,若是父類加載失敗,就調用本身的findClass
加載,咱們看一下這個方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
複製代碼
直接拋出異常,這說明findClass須要子類BaseDexClassLoader實現,以下:
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>();
//註釋1
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
,而後再註釋1處調用DexPathList
的findClass
方法
public Class<?> findClass(String name, List<Throwable> suppressed) { //註釋1
for (Element element : dexElements) {
//註釋2
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
複製代碼
在註釋1處遍歷dexElements
數組,在註釋2處調用Element
的findClass
方法
public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) {
System.err.println("Warning: Using deprecated Element constructor. Do not use internal"
+ " APIs, this constructor will be removed in the future.");
if (dir != null && (zip != null || dexFile != null)) {
throw new IllegalArgumentException("Using dir and zip|dexFile no longer"
+ " supported.");
}
if (isDirectory && (zip != null || dexFile != null)) {
throw new IllegalArgumentException("Unsupported argument combination.");
}
if (dir != null) {
this.path = dir;
this.dexFile = null;
} else {
this.path = zip;
this.dexFile = dexFile;
}
}
...
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
//註釋1
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
複製代碼
咱們從構造方法能夠看出,它內部封裝了DexFile
,他用於加載dex
,註釋1處若是dexFile
不爲null
則調用DexFile
的loadClassBinaryName
方法
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
複製代碼
又調用了defineClass
方法
private static Class defineClass(String name, ClassLoader loader, Object cookie, DexFile dexFile, List<Throwable> suppressed) {
Class result = null;
try {
//註釋1
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;
}
複製代碼
註釋1處調用了defineClassNative
方法來加載dex
相關文件,這個是native
方法不在向下分析
參考: 《Android進階解密》