上一篇文章我们了解了 Java
的 ClassLoader
,上一篇文章传送门JVM 类加载机制
其实 Android
中的 ClassLoader
和 java
中的是不一样的,因为 java
中的 CalssLoader
主要加载 Class
文件,但是 Android
中的 ClassLoader
主要加载 dex
文件
Android
中的 ClassLoader
分为俩种类型, 系统类加载器
, 自定义类加载器
。其中 系统的类加载器
分为三种, BootClassLoader
, PathClassLoader
, DexClassLoader
Android 系统启动时,会用BootClassLoader来预加载常用类,与java中的ClassLoader不同,他不是用C++实现,而是用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
的内部类,并继承自 ClassLoader
, BootClassLoader
是一个单例类,需要注意的是 BootClassLoader
是默认修饰符,只能包内访问,我们是无法使用的
DexClassLoader可以加载dex文件和包含dex文件的压缩文件(比如jar和apk文件),不管加载那种文件,最终都是加载dex文件,我们看一下代码
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有四个参数
/data/data/<Package Name>/...
DexClassLoade
继承自 BaseDexClassLoader
,所有的实现都是在 BaseDexClassLoader
中
Android
用 PathClassLoader
来加载系统类和应用程序类,代码如下:
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
中
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进阶解密》