动态加载的类型

CHSmileIP属地: 浙江
字数 1,825

现在网络上有许多关于动态加载的介绍的文章,谈及的关键词汇有动态加载插件化热部署热修复等,对于一些刚接触这方面开发技术的人来说,可能容易混淆。

动态加载的类型

无论是插件化、热部署还是热修复,这些技术的根源都可是说是动态加载,这也是我把 “动态加载” 作为这个系列文章主题的原因。

动态加载,就是在程序运行时,加载外部的可执行文件并运行。这里的运行时就是指应用冷启动并开始工作后;外部可以是可以是 SD 卡,可以是 data 目录,也可以是 jniLib 目录,这些可执行文件是没有随着应用一起编译的。

Android 的动态加载按照工作机制的不同,可以分为虚拟机层动态加载Native 层动态加载两大类。

运行在虚拟机

简单来说就是只用 JAVA 代码搞定的类型。

基于虚拟机的动态加载技术的核心是类加载器 ClassLoader,通过它我们能够加载一些新的类,这种方式也是目前大部分技术文章谈到的加载方式。其中,根据 ClassLoader 使用方式的不同,又演变出 “热部署”、“插件化”、“热修复” 等技术。

热部署

加载外部可执行文件的 ClassLoader 实例与原 APP 的 ClassLoader 实例是互相独立的(不在同一棵代理树上),加载进来的新的类与原 APP(宿主)里存在的类互相独立,根据 Java 对类的定义,因为这些类的 ClassLoader 不同,所以他们即便包名和类名一致,或者有继承关系,他们也属于不懂的类。所以以这种方式加载进来的类与原有的类不能互通,不能污染宿主原有的类,适合用来动态加载一些独立的业务,比如一些推广的游戏,在宿主上提供一个入口,用户不需要安装游戏就能运行。因为这种方式起到不用安装就能部署游戏的作用,所以称为热部署。

插件化

加载外部可执行文件的 ClassLoader 实例与宿主的 ClassLoader 实例不是互相独立的,用宿主的 ClassLoader 加载过的类就无法从外部可执行文件中再次加载,它们可以共用一个公共库,习惯上把外部可执行文件称为插件。插件里可以存放公共库里一些借口的实现类,可以有一些新的 Activity 或者 Service 等组件,可以把一些宿主里的业务挪到插件中,插件可以自主升级,不用随着宿主 APP 发版。

热修复

在使用插件化技术的同时,也可以使用插件中的新的类来替换宿主同名的类,这样就能修复宿主中原有的类存在的 BUG。相比插件化,热修复因为不需要考虑组件和 res 资源的问题,所以相对简单得许多,要保证插件种新的类的加载要在加载宿主中原有类的之前。

拆分 DEX

相信大家都知道打包 DEX 时 65536 方法数超标问题,也就是一个 DEX 只能有 65536 个方法,因此有了 multi-dex 的解决方案,把本来只有一个的 DEX,拆分成复数以上的 DEX,运行时挨个加载进来,这其实也算是一种动态加载,只不过实现过程对开发者是透明的。

除此之外,还有另一种拆分 DEX 是用于减少冷启动的时间的。冷启动是指应用第一次从用户点击到完成初始化工作的全部过程。随着现在 APP 的体积不断增长,一些 APP 的 DEX 文件十分庞大,APP 在启动的时候,单单加载所有的 DEX 文件就需要非常多的耗时,所以用户点击 APP 的时候会有一个明显的卡顿过程。因此有一种拆分 DEX 的方案是 “拆分一个启动闪屏用的 DEX,里面只存放启动闪屏界面需要用到的类,因此非常小,其他类放到其他 DEX 里面”,启动的时候因为只需要加载闪屏的 DEX,所以非常快,APP 进入闪屏后,通过异步任务去完成其他 DEX 的加载,就能消除卡顿的过程。

第一种拆分 DEX 是官方支持的,开发者只需要打开 multi-dex 功能即可;第二种拆分 DEX 则需要开发者自己设计。

基于 ClassLoader 的动态加载都有个共同的特点,就是新的类一旦加载进内存了,就无法再次替换了,所以无法在运行时候升级功能,需要重启 APP 才能生效。

运行在 Native

有另一种动态加载方式是工作在 Native 层的,相比于 ClassLoader,在 Native 层的动态加载不需要重新启动 APP 就能生效,这类的加载有 加载 SO 库 和 基于 JNI HOOK 的热修复。

加载 SO 库

加载 SO 库是最常见的 Native 动态加载,我们项目经常中使用 SO 库,编译 APP 的时候,SO 并不会参与编译,会原封不动被拷贝到 APK 包里的 lib 目录下,安装 APK 的时候,系统会扫描 lib 文件夹下支持当前设备 CPU 类型(比如 arm 或 x86)的 SO 库(APK 包会带有多种 CPU 类型对应的 SO 库,安装的时候只需要对应类型的)并拷贝到系统安装目录,APP 在运行时可以调用 System#loadLibrary方法动态加载对应的 SO 库,此外还可以调用System#load加载指定路径上的 SO 库。

现在的 APK 里面往往带有非常多的 SO 库,而 APP 运行时只需要用到对应 CPU 类型的 SO 库,因此把 SO 库从 APK 包里剥离出来也是 APK 瘦身的有效手段。

JNI HOOK

基于 JNI HOOK 的热修复技术的代表框架有阿里巴巴的 AndFix。Android 中,修复 BUG 的方式就是更新类的方法,和 ClassLoader 通过加载新的类来更换方法的实现的想法一样,AndFix 也是通过更换方法的做法来实现热修复,不过做法比较取巧。Android 中执行 Native 方法的时候,会去 SO 库中查找对应的 C/C++ 方法,而 AndFix 先把普通 Java 方法用 Native 方法代替,再通过更换不同 SO 库还更换 Native 方法的实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
1人点赞
CHSmile阳光、活力、自信
总资产5共写了10.1W字获得127个赞共102个粉丝

推荐阅读更多精彩内容

  • 动态加载技术 介绍 在程序运行的时候,加载一些程序自身原本不存在的可执行文件并运行这些文件里的代码逻辑。 动态调用...
    冰点k阅读 4,095评论 1 11
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,242评论 25 708
  • Android博客周刊专题之#插件化开发# 本期专栏目讨论插件化开发。插件化涉及的东西很多,所以我们需要多个维度去...
    sufun_wu阅读 6,804评论 2 58
  • 最近几年移动开发业界兴起了「 插件化技术 」的旋风,各个大厂都推出了自己的插件化框架,各种开源框架都评价自身功能优...
    斜杠时光阅读 4,002评论 1 36
  • 黑夜来临 我蜷缩在阴暗的角落 像极度惊恐的猫 拼命拱起嶙峋的背脊 那双萤绿色的 神秘、哀怨、而又凄凉的眼 望着这无...
    姀萧阅读 185评论 0 8