一. Java中的ClassLoader
1. 继承关系
-
ClassLoader
是一个抽象类,其中定义了ClassLoader
的主要功能。 -
SecureClassLoader
继承了抽象类ClassLoader
,但SecureClassLoader
并不是ClassLoader
的实现类,而是拓展了ClassLoader
类加入了权限方面的功能,加强了ClassLoader
的安全性。 -
URLClassLoader
继承自SecureClassLoader
,用来通过URl路径从jar文件和文件夹中加载类和资源。 -
ExtClassLoader
和AppClassLoader
都继承自URLClassLoader
,它们都是Launcher
的内部类,Launcher
是Java虚拟机的入口应用,ExtClassLoader
和AppClassLoader
都是在Launcher
中进行初始化的。
二. Android中的ClassLoader
1. 继承关系
-
ClassLoader
是一个抽象类,其中定义了ClassLoader
的主要功能。BootClassLoader
是它的内部类。 -
SecureClassLoader
继承了抽象类ClassLoader
。SecureClassLoader
并不是ClassLoader
的实现类,而是拓展了ClassLoader
类加入了权限方面的功能,加强了ClassLoader
的安全性。 -
URLClassLoader
类继承自SecureClassLoader
,用来通过URl路径从jar文件和文件夹中加载类和资源。 -
InMemoryDexClassLoader
是Android8.0新增的类加载器,继承自BaseDexClassLoader
,用于加载内存中的dex
文件。 -
BaseDexClassLoader
继承自ClassLoader
,是抽象类ClassLoader
的具体实现类,PathClassLoader
和DexClassLoader
都继承它。
2. 作用和分类
(1)作用:
Android虚拟机运行的是Dex字节码(将Class文件合并优化生成的产物),ClassLoader用来加载dex文件。
(2)分类
分为系统类加载器和自定义类加载器。主要的系统类加载器:
- BootClassLoader
- PathClassLoader
- DexClassLoader
三. Android中ClassLoader源码分析
相关源码:
/dalvik/system
1. ClassLoader
(1)构造函数:
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
ClassLoader
需要传入一个父ClassLoader
,如果父ClassLoader
不传的时候,通过
getSystemClassLoader
方法,创建了一个PathClassLoader
public static ClassLoader getSystemClassLoader() {
return SystemClassLoader.loader;
}
static private class SystemClassLoader {
public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}
private static ClassLoader createSystemClassLoader() {
String classPath = System.getProperty("java.class.path", ".");
String librarySearchPath = System.getProperty("java.library.path", "");
return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
}
(2)loadClass方法:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
//(1)先判断这个类是否被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
//(2)如果没被加载,先让父加载器进行加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//(3)如果父加载器加载失败,则自身进行加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
双亲委托机制:
- 除了顶层的类加载器外,其他的类加载器都有自己的父类加载器,在加载类时首先判断这个类是否被加载过,如果已经加载则直接返回。
- 如果未被加载过,则先尝试让父加载器进行加载,最终所有加载请求都会传递给顶层的加载器中。
- 当父加载器发现未找到所需的类而无法完成加载请求时,子加载器的findClass方法中进行加载。
2. BaseDexClassLoader
(1)构造:
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
this(dexPath, librarySearchPath, parent, null, false);
}
public BaseDexClassLoader(String dexPath,
String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
boolean isTrusted) {
super(parent);
this.sharedLibraryLoaders = sharedLibraryLoaders == null
? null
: Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
if (reporter != null) {
reportClassLoaderChain();
}
}
-
dexPath
:指目标类所在的APK或jar文件的路径,如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)
获得。 -
optimizedDirectory
:解压的dex文件存储路径,这个路径必须是一个内部存储路径,一般情况下使用当前应用程序的私有路径:/data/data//…。 -
librarySearchPath
:指目标类中所使用的C/C++库存放的路径
parent:父加载器。
(2)DexPathList
DexPathList
中维护着一个Element
数组,这个数组中Element
元素就是Dex文件(做热修复就要在这里做文章了)
#DexPathList
/*package*/ static class Element {
@UnsupportedAppUsage
private final File path;
/** Whether {@code path.isDirectory()}, or {@code null} if {@code path == null}. */
private final Boolean pathIsDirectory;
@UnsupportedAppUsage
private final DexFile dexFile;
private ClassPathURLStreamHandler urlHandler;
private boolean initialized;
@UnsupportedAppUsage
public Element(DexFile dexFile, File dexZipPath) {
if (dexFile == null && dexZipPath == null) {
throw new NullPointerException("Either dexFile or path must be non-null");
}
this.dexFile = dexFile;
this.path = dexZipPath;
// Do any I/O in the constructor so we don't have to do it elsewhere, eg. toString().
this.pathIsDirectory = (path == null) ? null : path.isDirectory();
}
public Element(DexFile dexFile) {
this(dexFile, null);
}
public Element(File path) {
this(null, path);
}
......
}
3. BootClassLoader
Android平台上所有ClassLoader
的最终parent。Android系统启动时会使用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的访问修饰符是默认的,只有在同一个包中才可以访问
4. PathClassLoader
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
@libcore.api.CorePlatformApi
public PathClassLoader(
String dexPath, String librarySearchPath, ClassLoader parent,
ClassLoader[] sharedLibraryLoaders) {
super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
}
使用PathClassLoader
来加载系统类和应用程序的类,通常用来加载已安装的apk的dex
文件
构造函数没有optimizedDirectory
,无法定义dex
文件路径,该参数默认值为/data/dalvik-cache
5. DexClassLoader
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
DexClassLoader
可以加载dex
文件以及包含dex的压缩文件(apk和jar文件),在其父类BaseDexClassLoader
里对".jar",".zip",".apk",".dex"后缀的文件最后都会生成一个对应的dex文件。
四. Android加载Class的过程
1. BaseDexClassLoader的findClass
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// First, check whether the class is present in our shared libraries.
if (sharedLibraryLoaders != null) {
for (ClassLoader loader : sharedLibraryLoaders) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}
}
}
// Check whether the class in question is present in the dexPath that
// this classloader operates on.
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
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的findClass方法
#DexPathList
private Element[] dexElements;
......
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
DexPathList
内部包含了一个DexFile
的数组dexElements
。类加载的过程,就是遍历这个数组调用了DexFile
的loadClassBinaryName
方法,最终调用native
方法defineClassNative
。
感谢https://blog.csdn.net/www851903307/article/details/87017662
网友评论