美文网首页
热修复原理学习-tinker,qzone

热修复原理学习-tinker,qzone

作者: echoSuny | 来源:发表于2020-03-29 23:32 被阅读0次

1.1 Dalvik与ART的区别
Dalvik:在Android5.0之前默认使用的是Dalvik。Dalvik是Google针对与Android平台开发的虚拟机。与JVM运行class文件不同的是Dalvik运行的是dex文件。dex是一种格式,是专为Dalvik设计的一种压缩格式。当dex文件被加载到Dalvik虚拟机当中时,会去解析dex文件得到所有的class然后去解释执行。在Dalvik当中还需要了解的一个概念时JIT(Just In Time)即时编译。由于Dalvik是需要解释执行的,如果全都去解释再执行的话,那么就会消耗很多时间,速度变慢。引入JIT就是为了可以将频繁运行的代码,也就是热点代码,可以在运行的时候编译成机器码。提升运行速度。
ART:ART即Android Run Time,是5.0之后才开始使用。ART与Dalvik不同的是多了一个步骤AOT(ahead of time),也就是预编译,即在应用安装的时候会提前把dex文件编译成OAT文件,将来程序运行的话就不需要去解释执行了,因此运行效率会更高,也会更省电,因为程序运行的时候不用重复编译了,减少了CPU的使用。缺点是安装APK的时候速度慢,就是因为多了AOT的步骤,把dex文件编译成了机器语言消耗了时间。另外ART是兼容Dalvik的。如果运行程序的时候使用的是OAT文件那么就按照ART正常的流程去执行。如果在运行的时候加载的是dex文件,那么就和Dalvik一样需要JIT和解释执行。

1.2 Dexopt和Dexaot
Dexopt:在Dalvik模式下,当dex文件被加载到Dalvik虚拟机中需要对dex文件进行验证和优化得到一个odex文件。(optimized dex)
Dexaot:在ART模式下,前两步和DexOpt是一样的,区别就是在变成odex之后需要进行AOT提前编译,编译成OAT文件

2.1Android中的ClassLoader


image.png

Android和Java中的ClassLoader是有区别的。Android加载的是dex文件,Java加载的则是class文件。其中需要重点关注的是PathClassLoader。其实不管是PathClassLoader还是DexClassLoader都只是继承BaseDexClassLoader并且重写了构造方法,没有增加其他的逻辑。

public DexClassLoader(String dexPath,String optimizedDirectory,String libraryPath, ClassLoader parent){
           super(dexPath,new File(optimizedDirectory),libraryPath, parent);
}
public PathClassLoader(String dexPath,String libraryPath, ClassLoader parent){
           super(dexPath,null,libraryPath, parent);
}

可以看到两者的区别就在于第二个参数。DexClassLoader需要传入一个优化的路径,也就是存放odex的路径。需要注意的是优化的路径不是随意的,必须是程序的私有目录,sdcard不可以。而PathClassLoader则传入了null。传了null不代表不优化,而是用了应用程序的私有目录/data/dalvik-cache下。

2.2 双亲委托机制
定义:某个类加载器在加载类时,首先将加载任务交给父加载器,依次递归,如果父加载器可以完成加载任务就返回,如果父加载器失败或者没有父加载器才自己去加载。(这里的父加载器不是指的BaseDexCLassLoader而是指的构造参数当中的ClassLoader parent)

ClassLoader.class

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) {
                    // 自己去找
                    c = findClass(name);
                }
            }
            return c;
    }
// 在ClassLoader这个类中的findClass方法只是扔了一个异常,并没有实现,在子类当中实现了
protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
//------------------------------------分割线----------------------------------------
BaseDexClassLoader.class

public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super(parent);
       // 构建DexPathList对象
        this.pathList = new DexPathList(this, dexPath, librarySearchPath ,optimizedDirectory);
    }

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        // 真正执行findClass的是DexPathList
        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.class

public Class<?> findClass(String name, List<Throwable> suppressed) {
       // 从Element[] 数组当中遍历寻找
        // 而Element[]是由方法下面的makeDexElements生成的
        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;
    }

/**
   *  files : 所有的dex文件
   *  optimizedDirectory :优化的目录
   *  suppressedExceptions :异常
   *  loader :类加载器
   */
 private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
            List<IOException> suppressedExceptions, ClassLoader loader) {
      Element[] elements = new Element[files.size()];
      int elementsPos = 0;
     
      for (File file : files) {
          if (file.isDirectory()) {
              elements[elementsPos++] = new Element(file);
          } else if (file.isFile()) {
              String name = file.getName();
              // 文件是否以 .dex结尾
              if (name.endsWith(DEX_SUFFIX)) {
                  try {
                      //   调用loadDexFile来加载dex文件
                      DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
                      if (dex != null) {
                         // 把DexFile对象传入到Element对象中
                          elements[elementsPos++] = new Element(dex, null);
                      }
                   ......
      return elements;
    }

 private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
                                       Element[] elements)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file, loader, elements);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
        }
    }

//所以最终在上面遍历Element的findClass最终实际是调用了DexFile内部的native函数来找到需要加载的类的。

经过了上面的层层调用可以发现一个转换的步骤就是:dex->DexFile->Element。所以可以把Element当作一个dex文件。Element[]就是一个dex数组。接着循环遍历Element数组来找到要加载的类,那么只需要在数组的前面插入我们要修复的类的dex就可以达到修复的目的了。如图所示:


image.png

相关文章

  • 热修复原理学习-tinker,qzone

    1.1 Dalvik与ART的区别Dalvik:在Android5.0之前默认使用的是Dalvik。Dalvik是...

  • Tinker热修复原理及原理样例

    Tinker热修复原理适用范围和机型都是比较广的热修复 Tinker和andfix和sophix,Dexposed...

  • tinker

    #微信热修复框架Tinker 接入指南----------> Tinker简介:By腾讯微信团队开发,技术原理简介...

  • 2018 深入解析Android热修复技术

    本文框架 什么是热修复? 热修复框架分类 技术原理及特点 Tinker框架解析 各框架对比图 总结 通过阅读本文,...

  • Android热修复技术原理详解(最新最全版本)

    本文框架 什么是热修复? 热修复框架分类 技术原理及特点 Tinker框架解析 各框架对比图 总结 通过阅读本文,...

  • 热修复 -- Sophix & Tinker

    一、Sophix & Tinker 二、使用 Sophix使用 Tinker使用 三、原理 代码修复 代码修复有两...

  • 移动架构02-Tinker热修复

    移动架构02-Tinker热修复 Tinker是目前开源框架中兼容性最好的热修复框架。 Tinker的Github...

  • android前沿技术

    一、热升级 Tinker源码解析与手写 二、热修复 阿里云hotfix使用和原理 三、组件化 组件化原理和ARou...

  • Tinker热修复原理

    一、tinker热修复方案 tinker热修复是一种冷启动的修复,就是需要用户重新启动app,因为这样才能让Cla...

  • Tinker热修复原理

    当前市面的热补丁方案有很多,其中比较出名的有阿里的AndFix、美团的Robust以及QZone的超级补丁方案。但...

网友评论

      本文标题:热修复原理学习-tinker,qzone

      本文链接:https://www.haomeiwen.com/subject/jhseuhtx.html