ClassLoader 在热修复中的应用
class Class<T> {...private transient ClassLoader classLoader;...}
? ClassLoader 是一个抽象类,而它的具体实现类主要有:
BootClassLoader
用于加载 Android Framework 层 class 文件。
PathClassLoader
用于 Android 应用程序类加载器。可以加载指定的 dex,以及 jar、zip、apk 中的 classes.dex
DexClassLoader
用于加载指定的 dex,以及 jar、zip、apk 中的 classes.dex
很多博客里说 PathClassLoader 只能加载已安装的 apk 的 dex,其实这说的应该是在 dalvik 虚拟机上。
但现在一般不用关心 dalvik 了。
Log.e(TAG, "Activity.class 由:" + Activity.class.getClassLoader() +" 加载");Log.e(TAG, "MainActivity.class 由:" + getClassLoader() +" 加载");
//输出:Activity.class 由:java.lang.BootClassLoader@d3052a9 加载
MainActivity.class 由:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.enjoy.enjoyfix-1/base.apk"],nativeLibraryDirectories=[/data/app/com.enjoy.enjoyfix-1/lib/x86, /system/lib, /vendor/lib]]] 加载
它们之间的关系如下:
PathClassLoader
与DexClassLoader
的共同父类是BaseDexClassLoader
。
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory,String librarySearchPath, ClassLoader parent) {super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);}}
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);}}
可以看到两者唯一的区别在于:创建DexClassLoader
需要传递一个optimizedDirectory
参数,并且会将其创建为File
对象传给super
,而PathClassLoader
则直接给到 null。因此两者都可以加载指定的 dex,以及 jar、zip、apk 中的 classes.dex
PathClassLoader pathClassLoader = new PathClassLoader("/sdcard/xx.dex", getClassLoader());
File dexOutputDir = context.getCodeCacheDir();DexClassLoader dexClassLoader = new DexClassLoader("/sdcard/xx.dex",dexOutputDir.getAbsolutePath(), null,getClassLoader());
? 其实,optimizedDirectory
参数就是 dexopt 的产出目录(odex)。那PathClassLoader
创建时,这个目录为 null,就意味着不进行 dexopt?并不是,optimizedDirectory
为 null 时的默认路径为:/data/dalvik-cache。
在 API 26 源码中,将 DexClassLoader 的 optimizedDirectory 标记为了 deprecated 弃用,实现也变为了:
public DexClassLoader(String dexPath, String optimizedDirectory,String librarySearchPath, ClassLoader parent) {super(dexPath, null, librarySearchPath, parent);}
......和 PathClassLoader 一摸一样了!
双亲委托机制
? 可以看到创建ClassLoader
需要接收一个ClassLoader parent
参数。这个parent
的目的就在于实现类加载的双亲委托。即:
? 某个类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
// 检查 class 是否有被加载
Class c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {//如果 parent 不为 null,则调用 parent 的 loadClass 进行加载
c = parent.loadClass(name, false);} else {//parent 为 null,则调用 BootClassLoader 进行加载
c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {
}
if (c == null) {// 如果都找不到就自己查找 long t1 = System.nanoTime();c = findClass(name);}}return c;}
因此我们自己创建的 ClassLoader:
new PathClassLoader("/sdcard/xx.dex", getClassLoader());
并不仅仅只能加载 xx.dex 中的 class。
?
值得注意的是:
c = findBootstrapClassOrNull(name);
按照方法名理解,应该是当 parent 为 null 时候,也能够加载
BootClassLoader
加载的类。
new PathClassLoader("/sdcard/xx.dex", null)
,能否加载 Activity.class?但是实际上,Android 当中的实现为:(Java 不同)
private Class findBootstrapClassOrNull(String name){return null;}
findClass
? 可以看到在所有父 ClassLoader 无法加载 Class 时,则会调用自己的findClass
方法。findClass
在 ClassLoader 中的定义为:
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}
? 其实任何 ClassLoader 子类,都可以重写loadClass
与findClass
。一般如果你不想使用双亲委托,则重写loadClass
修改其实现。而重写findClass
则表示在双亲委托下,父 ClassLoader 都找不到 Class 的情况下,定义自己如何去查找一个 Class。而我们的PathClassLoader
会自己负责加载MainActivity
这样的程序中自己编写的类,利用双亲委托父 ClassLoader 加载 Framework 中的Activity
。说明PathClassLoader
并没有重写loadClass
,因此我们可以来看看 PathClassLoader 中的 findClass
是如何实现的。
public BaseDexClassLoader(String dexPath, File optimizedDirectory,String librarySearchPath, ClassLoader parent) {super(parent);this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);}
@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {List<Throwable> suppressedExceptions = new ArrayList<Throwable>();//查找指定的 classClass 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;}
? 实现非常简单,从pathList
中查找 class。继续查看DexPathList
public DexPathList(ClassLoader definingContext, String dexPath,String librarySearchP
ath, File optimizedDirectory) {//.........// splitDexPath 实现为返回 List<File>.add(dexPath)// makeDexElements 会去 List<File>.add(dexPath) 中使用 DexFile 加载 dex 文件返回 Element 数组 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,suppressedExceptions, definingContext);//.........
}
public Class findClass(String name, List<Throwable> suppressed) {//从 element 中获得代表 Dex 的 DexFilefor (Element element : dexElements) {DexFile dex = element.dexFile;if (dex != null) {
评论