Android热修复之代码修复原理(类加载方案)

这个方案是基于Android的类加载机制
PathClassLoader负责加载Android的系统类和你自己定义的应用程序类;
DexClassLoader负责加载任意目录下的dex和zip、jar、apk内的dex文件;
PathClassLoader0DexClassLoader都继承自BaseDexClassLoader,这两者类似,只是增加了调用父类构造函数的构造函数。所以功能不同只是因为定义,是设计人员要求这做,其实PathClassLoader能做的,DexClassLoader也能做

类加载流程

首先我们要了解类加载的流程,加载类的方法就是loadClass()

  1. 调用loadClass(),查看此类是否已经被加载过;是,直接返回结果;不是,跳到步骤2
  2. 如果父类不为null,调用父类加载器的loadClass();如果父类为null,调用当前类加载器的findClass()加载,这是真正加载类的方法。
  3. 最后返回结果

这里的父类指的是类加载其中的属性ClassLoader parent;而不是继承关系。
BaseDexClassLoader继承了ClassLoader,重写了findClass()。我们看看这个方法干了什么。

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            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;
        }

重要的是这一行Class c = pathList.findClass(name, suppressedExceptions);,很显然,类加载的操作是在这里完成的。
pathList是BaseDexClassLoader的一个属性,pathList里面还有一个Element数组,你可以把每一个Elment看成是一个.dex文件。

		//DexPathList.class
        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;
        }

pathList遍历了Element数组,依次调用Element.findClass(),这个源码就不看了,反正如果这个Element元素是我们要加载的类就会返回正确结果,否则继续下一个。

那我们现在就知道了,Android系统和应用程序类加载时,会去加载pathList内的一个个Element,如果找到对应的.dex就会返回。
那么我们就可以利用这一点,首先DexClassLoader跟PathClassLoader都继承自BaseDexClassLoader,所以我们可以利用DexClassLoader去加载我们需要替换的新类,然后通过反射获得DexClassLoader和PathClassLoader的pathList,再获得Element集合,最后合并两个集合,这里要注意,DexClassLoader的Element集合需要放在前面。
这就是代码替换的原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值