Android ClassLoader机制与热修复

 

Android中的ClassLoader:

BootClassLoader:

加载Android sdk中的类;

PathClassLoader:

加载已安装应用内的类;

DexClassLoader:

加载外部的类;

双亲委派机制:

loadclass("com.a.b.c")方法只有一个实现,在顶层ClassLoader中;

注意:父Parent并不是继承关系;如:PathClassLoader在双亲委派机制中的父Parent是BootClassLoader; DexClassLoader父Parent是PathClassLoader;

某个类加载器加载类时,首先从内存中找这个类,若没找到就将加载任务委派给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成加载任务或者没有父加载器时,才自己去加载,自己没有加载出来,就去存放所有dex的dexElements中寻找class。

dexElements就是我们需要hook到的变量,往数组最前面插新的dex;

public abstract class ClassLoader {

 ...

 private final ClassLoader parent;  
 
 ...

 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) {
                   
                }

                if (c == null) {
                    //从存放所有dex的集合dexElements中找class
                    c = findClass(name);
                }
            }
            return c;
    }

...

}
public class BaseDexClassLoader extends ClassLoader {

    private final DexPathList pathList;
    
    ...

    @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;
    }
    
    ...
}

final class DexPathList {
    ...

    //存放所有dex的集合
    private Element[] dexElements;

    ...
    
    //从存放所有dex的集合dexElements中找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;
    }
    
    ...
}

阿里sophix

方法执行过程:

  1. 调用对象.method()
  2. 从对象.method()获取方法的指令集地址
  3. 执行引擎根据指令集地址从class中拿到方法指令
  4. 将方法指令的栈帧放入栈中
  5. 进行执行栈帧得到结果

原理:c++替换方法区的class中有异常的arm指令地址;

优势:不需要重启app;

劣势:兼容性差,需要各个版本对应的头文件art_mothod.h;

一个java方法在ART虚拟机中对应一个ArtMethod结构体;

ArtMethod中有一个指针void * entry_point_from_quick_compiled_code_,指向了方法编译后的arm指令地址;

修改这个指针指向的有异常的地址,将地址改为正常的地址,就修改了要执行了arm指令;

腾讯Tinker

原理:class双亲委派机制,使用PathClassloader进行dex加载和插队;

优势:兼容性好;

劣势:需要重启app;

修复流程:

  1. 开发者将新的apk和老的有Bug的apk使用tinker的gradle插件进行差分打包(类似技术如bsdiff),生成体积很小的patch文件;
  2. 下载patch文件至app安装的私有目录;
  3. 后台开启新进程,将有bug的dex和patch进行合并,生成新的dex(里面是正常的无bug类);
  4. 拿到application的ClassLoader:PathClassLoader
  5. 使用PathClassLoader,通过反射从父类查找pathList对象
  6. 从pathList对象中反射得到dexElements
  7. 将新的dex放入数组dexElements最前面;
  8. 重启App,使用双亲委托机制,会先加载新的dex中的类,不再加载之后的dex中的bug类;实现修复bug;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值