记:初学习插件化和热更新

本篇文章学习于以下链接,主意为用自己方式理解和记录
动态加载技术插件化讲解:转载于https://segmentfault.com/a/1190000004062866
插件化和热更新的小梳理:转载于https://www.jianshu.com/p/704cac3eb13d

简单来说,
插件化意为:把需要实现的功能或模块提取出来,减少APK的大小,在需要使用的地方,再进行相应的加载
热更新意为:主要从修复bug的角度考虑,不采用传统的下载、安装apk的方式,在用户不知不觉中修改bug
(本篇学习文章侧重点在热更新)

一.插件化

实际应用场景:动态换肤、动态加载资源、或宿主APK体积太大,把不是必须的模块动态加载
优点:
(1)将项目可以模块化分开,降低了耦合度,可以并行开发,加快了开发速度
(2)可以避免安卓市场的规则
(3)减少主项目的方法数,可以避免65536的问题
缺点:
(1)并行开发后合并模块,有可能会出现各种奇怪的问题
(2)动态加载的插件会出现不兼容的问题在一些老的版本手机

二.热更新

热更新的作用是为了及时迅速的修改线上版本的bug
热更新中,最重要的就是类加载器
类加载器包括PathClassLoaderDexClassLoader,源码贴于下面:

public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}
   public class DexClassLoader extends BaseDexClassLoader {
        public DexClassLoader(String dexPath, String optimizedDirectory,
                              String librarySearchPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
        }
    }
    public class BaseDexClassLoader extends ClassLoader {
    ...
        public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent){
            super(parent);
            this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }
    ...
    }

加上后期理解可知:
(1)PathClassLoaderDexClassLoader都继承于BaseDexClassLoader
(2)PathClassLoader只能加载已经安装到APP系统中的文件(data/app目录);DexClassLoader可以安装任意目录下的dex/jar/apk/zip文件
(3)DexClassLoader相比于PathClassLoader多一个optimizedDirectory的参数。由第二点可知道,DexClassLoader允许安装dex/jar/apk/zip文件,而他们本质都是压缩文件,所以知道多的
optimizedDirectory参数是一个解压缩后的目录
(4)类加载器通过DexPathList的findClass()方法找到他说加载到的class,其中DexPathList的构造器使用makeDexElements()得到Element集合,形式为用“;”分号隔开的file文件目录拼接的字符串

原理
经过对PathClassLoader、DexClassLoader、BaseDexClassLoader、DexPathList的分析,我们知道,安卓的类加载器在加载一个类时会先从自身DexPathList对象中的Element数组中获取(Element[] dexElements)到对应的类,之后再加载。采用的是数组遍历的方式,不过注意,遍历出来的是一个个的dex文件。
  在for循环中,首先遍历出来的是dex文件,然后再是从dex文件中获取class,所以,我们只要让修复好的class打包成一个dex文件,放于Element数组的第一个元素,这样就能保证获取到的class是最新修复好的class了(当然,有bug的class也是存在的,不过是放在了Element数组的最后一个元素中,所以没有机会被拿到而已)。

《记:初学习插件化和热更新》 原理 .png

摘自:
https://juejin.im/post/5a0ad2b551882531ba1077a2

我的理解:在父类BaseDexClassLoader中,通过DexPathList的构造方法调用makeDexElements()方法获取Elements集合。其中集合中的每个Elements对应一个dex,一个dex对应多个Class文件。然后通过DexPathList的findClass()方法,使用刚才获取到的Elements集合,找到对应的Class文件。我们要做的就是把补丁,放入elment集合的最前面,然后把这个组合成一个新的集合,包装成一个dex。通过反射放入DexPathList。由于双亲加载机制的特点,当找到第一个Class,后面的错误有Bug的Class将不会再加载,这样就实现了热更新。
双亲加载机制:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己加载

《记:初学习插件化和热更新》 图解.jpg

总结:热更新使用的是类加载机制。类加载包含PathClassLoader和DexClassLoader,它们都继承于BaseDexClassLoader,不同之处在于PathClassLoader只能加载安卓默认安装目录下的文件即(data/apk)中,而DexClassLoader可以安装任意目录下的文件(即dex/apk/jar/zip)。DexClassLoader会比PathClassLoader构造方法多一个参数,是用于存储解压文件的目录。在BaseDexClassLoader中,构造方法获取到dexPathList,又通过dexPathList构造方法的makeDexElements()得到Element集合,每个element对应一个dex文件,一个dex文件对应多个class。dexPathList中的findClass能帮助我们从makeDexElements()得到的Element集合找到有问题的Class。我们要做的就是把补丁Elment数组和原来的Elment数组整合成一个新的数组(补丁在前),然后把这个Elment数组给到dexPathList的Element集合中去。这样由于双亲加载机制,只会加载一次class,这样错误的bug的class仍在里面但一直不会加载到。

    原文作者:爱吃板栗的小女孩
    原文地址: https://www.jianshu.com/p/8f825d50da89
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞