ClassLoader 的類型
- java 中的ClassLoader 加載的是jar 和class文件
- Android 中的ClassLoader 加載的是dex文件
兩種ClassLoader類型
- 系統類加載器
- 自定義加載器
系統類加載器 (3種)
- BootClassLoader
- PathClassLoader
- DexClassLoader
BootClassLoader
- Android 系統啓動時會使用BootClassLoader來預加載常用類,而BootClassLoader是由java實現的
- java 中的BootstrapClassLoader 是由C/C++代碼實現的
BootClassLoader 代碼
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 的內部類
- BootClassLoader 繼承自ClassLoader
- BootClassLoader是個單例類
- BootClassLoader 的訪問修飾符是默認的,只有在同一個包中纔可以訪問,
DexClassLoader
用途
- DexClassLoader 可以加載dex文件
- DexClassLoader也可以加載包含dex的壓縮文件(apk 和jar文件)
DexClassLoader源碼如下
public class DexClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code DexClassLoader} that finds interpreted and native
* code. Interpreted classes are found in a set of DEX files contained
* in Jar or APK files.
*
* <p>The path lists are separated using the character specified by the
* {@code path.separator} system property, which defaults to {@code :}.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param optimizedDirectory directory where optimized dex files
* should be written; must not be {@code null}
* @param libraryPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be
* {@code null}
* @param parent the parent class loader
*/
public DexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
DexClassLoader繼承自BaseDexClassLoader,方法都在BaseDexClassLoader中實現
DexClassLoader 的構造方法有如下4個參數
- dexPath:dex相關文件路徑集合,多個路徑用文件分隔符分隔,默認文件分隔符爲":"
- optimizedDirectory:解壓的dex文件存儲路徑,這個路徑必須是一個內部存儲路徑:如/data/data//…
- librarySearchPath:包含C/C++庫的路徑集合,多個路徑用文件分隔符分隔,可以爲null
- parent:父加載器
PathClassLoader
用途:
- PathClassLoader 用來加載系統類和應用程序的類
PathClassLoader的代碼
/**
* Provides a simple {@link ClassLoader} implementation that operates on a list
* of files and directories in the local file system, but does not attempt to
* load classes from the network. Android uses this class for its system class
* loader and for its application class loader(s).
*/
public class PathClassLoader extends BaseDexClassLoader {
/**
* Creates a {@code PathClassLoader} that operates on a given list of files
* and directories. This method is equivalent to calling
* {@link #PathClassLoader(String, String, ClassLoader)} with a
* {@code null} value for the second argument (see description there).
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param parent the parent class loader
*/
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
/**
* Creates a {@code PathClassLoader} that operates on two given
* lists of files and directories. The entries of the first list
* should be one of the following:
*
* <ul>
* <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
* well as arbitrary resources.
* <li>Raw ".dex" files (not inside a zip file).
* </ul>
*
* The entries of the second list should be directories containing
* native library files.
*
* @param dexPath the list of jar/apk files containing classes and
* resources, delimited by {@code File.pathSeparator}, which
* defaults to {@code ":"} on Android
* @param libraryPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be
* {@code null}
* @param parent the parent class loader
*/
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
}
- PathClassLoader 繼承自BaseDexClassLoader,也都在BaseDexClassLoader中實現
- 在PathClassLoader的構造方法中沒有參數optimizedDirectory,這是因爲PathClassLoader已經默認了參數optimizedDirectory的值爲/data/dalvik-cache
- PathClassLoader無法自定義解壓dex文件存儲路徑
- PathClassLoader通常用來加載已經安裝的apk的dex文件(安裝的apk的dex文件會存儲在/data/dalvik-cache中)
ClassLoader 的繼承關係
先看圖再解釋
- ClassLoader 是一個抽象類,其中定義了ClassLoader的主要功能,BootClassLoader是它的內部類
- SecureClassLoader 類和JDK8中的SecureClassLoaderL類的代碼是一樣的,它集成了抽象類ClassLoader,SecureClassLoader 並不是ClassLoader 的實現類,而是擴展了ClassLoader類加入了權限方面的功能,加強了ClassLoader的安全性
- URLClassLoader 類和JDK8 中的URLClassLoader類代碼是一樣的,它繼承自SecureClassLoader,用來通過URL路徑從jar文件和文件夾中加載類和資源
- InMemoryDexClassLoader 是Android 8.0 新增的類加載器,繼承自BaseDexClassLoader,用於加載內存中的dex文件
- BaseDexClassLoader 繼承自ClassLoader ,是抽象類ClassLoader的具體實現類,PathClassLoader,DexClassLoader和InMemoryDexClassLoader 都繼承自它
ClassLoader 的加載過程
- Android 的ClassLoader 同樣遵循雙親委託模式
- ClassLoader的加載方法爲 loadClass
- loadClass方法被定義在抽象類ClassLoader中
ClassLoader -> loadClass 方法源碼
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded 首先,檢查類是否已經加載
Class c = findLoadedClass(name);//1
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {//2
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);//3
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {//4
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);//5
// this is the defining class loader; record the stats
}
}
return c;
}
以下對上面代碼的說明
- 註釋1:檢查傳入的類是否已經加載,如果已經加載就返回該類
- 註釋2:判斷父加載器是否存在,存在就調用父加載器的loadClass方法,如果不存在就調用註釋3處的findBootstrapClassOrNull 方法,這個方法會直接返回null
- 如果註解4處代碼成立,說明向上委託流程沒有檢查出類已經被加載,就會執行註釋5處的findClass方法來進行查找流程
下面爲註釋5處的findClass 方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
上面的findClass方法中直接拋出異常, 說明需要子類實現,下面爲BaseDexClassLoader的代碼
public class BaseDexClassLoader extends ClassLoader {
// 需要加載的dex列表
private final DexPathList pathList;
// dexPath要加載的dex文件所在的路徑,optimizedDirectory是odex將dexPath
// 處dex優化後輸出到的路徑,這個路徑必須是手機內部路勁,libraryPath是需要
// 加載的C/C++庫路徑,parent是父類加載器對象
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent, boolean isTrusted) {
super(parent);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
if (reporter != null) {
reportClassLoaderChain();
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
// 使用pathList對象查找name類
Class c = pathList.findClass(name, suppressedExceptions);//1
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;
}
}
- 在註釋 1 的地方調用了 DexPathList 的findClass方法
下面看下DexPathList 中的 findClass 方法
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {//1
Class<?> clazz = element.findClass(name, definingContext, suppressed);//2
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
- 註釋1處遍歷Element 數組dexElements
- 註釋2處調用Element的findClass方法
- Element是 DexPathList 的靜態內部類
Element 源碼如下
/*package*/ static class Element {
/**
* A file denoting a zip file (in case of a resource jar or a dex jar), or a directory
* (only when dexFile is null).
*/
private final File path;
private final DexFile dexFile;
private ClassPathURLStreamHandler urlHandler;
private boolean initialized;
/**
* Element encapsulates a dex file. This may be a plain dex file (in which case dexZipPath
* should be null), or a jar (in which case dexZipPath should denote the zip file).
*/
public Element(DexFile dexFile, File dexZipPath) {
this.dexFile = dexFile;
this.path = dexZipPath;
}
public Element(DexFile dexFile) {
this.dexFile = dexFile;
this.path = null;
}
public Element(File path) {
this.path = path;
this.dexFile = null;
}
.........
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;//1
}
- 從Element 的構造方法可以看出,其內部封裝了DexFile,它用於加載dex
- 在註釋1處如果DexFile不爲null就調用 DexFile 的loadClassBinaryName 方法
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);//1
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
- 在loadClassBinaryName 方法中調用了defineClass方法
- 註解1處調用了defineClassNative方法來加載dex相關文件,這個方法是Native方法
到此ClassLoader 的查找流程完成,可以概括爲下圖