美文网首页
Dex热修复原理

Dex热修复原理

作者: ppjuns | 来源:发表于2018-02-27 17:09 被阅读217次

    市场上热修复有两种一种是基于multidex的更新修复(比如tinker),另外一种是native hook(比如dexposed),tinker这种是反射获取dexelements数组,修改dex加载顺序。今天我们主要介绍dex这种。
    热修复包括两个部分

    1. 从远程端下载修复好bug的补丁包
    2. 客户端安装补丁包,加载补丁包的类。

    使用android�类加载器,在类没被加载到模拟器前(一般在application热修复,如果类已加载,再去记载相同的类就无效了)然后先加载补丁dex,再去加载原来的app里面的dex,因为加载过的类
    不会被加载第二次,从而做到热修复。

    1. 类加载器

    android的类加载器分为PathClassLoader和DexClassLoader.他们两者之间的区别就是

    1. PathClassLoader只能加载安装到手机里面的dex(比如data/app/包名里面的dex)
    2. DexClassLoader 可以加载任意目录下的dex/jar/apk/zip.

    下面我们从源码看看他们到底有什么不同

    public class PathClassLoader extends BaseDexClassLoader {
       
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
       }
    
       
       public PathClassLoader(String dexPath, String libraryPath,
               ClassLoader parent) {
            super(dexPath, null, libraryPath, parent);
    }
    
    
    //分割线
        public class DexClassLoader extends BaseDexClassLoader {
            public DexClassLoader(String dexPath, String optimizedDirectory,
              String libraryPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), libraryPath, parent);
       }
    }
    

    从代码可以看到两个classloader都继承了BaseDexClassLoader
    调用了父类的构造方法,dexclassloader多传入了一个optimizedDirectory文件,然后从注解可以看到optimizedDirectory参数是dex输出的文件。接下来我们再看basedexclassloader里面做了什么骚操作。

    public class BaseDexClassLoader extends ClassLoader {
         private final DexPathList pathList;
     public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(parent);
           this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
       }
    
       @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;
        }
    
        @Override
        protected URL findResource(String name) {
            return pathList.findResource(name);
        }
    
        @Override
        protected Enumeration<URL> findResources(String name) {
            return pathList.findResources(name);
       }
    
       @Override
        public String findLibrary(String name) {
           return pathList.findLibrary(name);
       }
       ....
    

    BaseDexClassLoader的构造参数分别是

    1. 一系列的补丁包路径
    2. 补丁包输出路径
    3. 加载用到库的路径,比如c 那些native库
    4. 父加载器

    其中DexPathList通过这四个参数完成初始化。最后通过findclass方法 里面的DexPathList.findClass来返回类。

    2. DexPathList源码

    接着看DexPathList源码里面做了什么
    首先看构造函数

    Element[] dexElements;
    public DexPathList(ClassLoader definingContext, String dexPath,
                String libraryPath, File optimizedDirectory) {
                    //省略参数的判断
    
    this.definingContext = definingContext;
           ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
           this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                             suppressedExceptions);
           if (suppressedExceptions.size() > 0) {
                this.dexElementsSuppressedExceptions =
                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
            } else {
                dexElementsSuppressedExceptions = null;
            }
           this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
    
    }
    

    从代码可以看到给classloader赋值,创建一个异常的list,给dexelements赋值,这里的dexelements是一个数组。来看看splitDexPath到底做什么了。
    看到splitAndAdd方法会根据:来截取字符串,就是多个dexpath之间用:分割,然后变成file,被加进去List<File> flies里面。最后看makeDexElements方法,
    遍历files,首先判断是否文件夹,是的话全部加进去elements,最后装转成ELement数组。
    最后在DexPathList的findclass方法,将Element的数组每个Element对象的dex加载成class。

    所以我们在什么时候插入补丁包的dex呢,就在Dexpatchlist类的findclass方法里面。将补丁的Elements数组加上原来的Elements数组,一起循环获取dex返回class。我们可以通过反射去获取elements数组。然后合并数组。调用findclass方法。以上就是multidex热修复的原理。

    相关文章

      网友评论

          本文标题:Dex热修复原理

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