前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dex热修复原理

Dex热修复原理

作者头像
ppjun
发布2018-09-05 11:58:47
1.1K0
发布2018-09-05 11:58:47
举报
文章被收录于专栏:ppjun专栏ppjun专栏

市场上热修复有两种一种是基于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.

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

代码语言:javascript
复制
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里面做了什么骚操作。

代码语言:javascript
复制
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源码里面做了什么 首先看构造函数

代码语言:javascript
复制
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热修复的原理。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.02.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 类加载器
  • 2. DexPathList源码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档