1. android 热部署是什么意思
在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作。对于某些大型的应用来说,每次的重启都需要花费大量的时间成本。虽然 osgi 架构的出现,让模块重启成为可能,但是如果模块之间有调用关系的话,这样的操作依然会让应用出现短暂的功能性休克。本文将探索如何在不破坏 Java 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某个类的更新。
类加载的探索
首先谈一下何为热部署(hotswap),热部署是在不重启 Java 虚拟机的前提下,能自动侦测到 class 文件的变化,更新运行时 class 的行为。Java 类是通过 Java 虚拟机加载的,某个类的 class 文件在被 classloader 加载后,会生成对应的 Class 对象,之局差巧后就可以创建该类的实例。默认的虚拟机行为只会在启动时加载类,如果后期有一个类需要更新的话,单纯替换编译的 class 文件,Java 虚拟机是不会更新正在运行的 class。如果要实现热部署,最根本的方式是修改虚拟机的源代码,改变 classloader 的加载行为,使虚拟机能监听 class 文件的更新,重新加载 class 文件,这样的行为破坏性很大,为后续的 JVM 升级埋下了一个大坑。
另一种友好的方法是创建自己的 classloader 来加载需要监听的 class,这样就能控制类加载的时机,从而实现热部署。本文将具体探索如何实现这个方案。首先需要了解一下 Java 虚拟机现有的加载机制。目前的加载机制,称为双亲委派,系统在使用一个 classloader 来加载类时,会先询问当前 classloader 的父类是否有能力加载,如果父类无法实现加载操作,才会将任务下放到该 classloader 来加载。这种自上而下的加载方式的好处是,让每个 classloader 执行自己的加载任务,不会重复加载类。但是这种方式却使加载顺序非常难改变,让自定义 classloader 抢先加载需要监听改变的类成为了一个难题。
不过我们可以换一个思路,虽然无法抢先加载该类,但是仍然可以用自定义 classloader 创建一个功能相同的类,让每次实例化的对象都指向这个新的类。当这个类的 class 文件发生改变的时候,再次创建一个更新的类,之后如果系统再次发出实例化请求,创建的对象讲指向这个全新的类。
下面来简单列举一下需要做的工作。
创建自定义的 classloader,加载需要监听改变的类,在 class 文件发生改变的时候,重新加载该类。
改变创建对象的行为,使他们在创建时使用自定义 classloader 加载的 class。
自定义加载器的实现
自定义加载器仍然庆态需要执行类加载的功能。这里却存在一个问题,同一个类加载器无法同时加载两个相同名称的类,由于不论类的结构如何发生变化,生成的类名不会变,而 classloader 只能在虚拟机停止前销毁已经加载的类,这样 classloader 就无法加载更新后的类了。这里有一个小技巧,让每次加载的类都保存成一个带有版本信息的 class,比如加载 Test.class 时,保存在内存中的类是 Test_v1.class,当类发生改变时,重新加载的类名是 Test_v2.class。但是真正执行加载 class 文件创建 class 的 defineClass 方法是一个 native 的方法,修改起来又变得很困难。所以面前还剩一条路,那就是直接修改编译生成的 class 文件。
利用 ASM 修改 class 文件
可以修改字节码的框架有很多,比如 ASM,CGLIB。本文使用的是 ASM。先来介绍一下 class 文件的结构,class 文件包含了以下几类信息,一个是类的基本信息,包含了访问权限信息,类名信息,父类信息,接口信息。第二个是类的变量信息。第三个是方法的信息。桐键ASM 会先加载一个 class 文件,然后严格顺序读取类的各项信息,用户可以按照自己的意愿定义增强组件修改这些信息,最后输出成一个新的 class。
首先看一下如何利用 ASM 修改类信息。
清单 1. 利用 ASM 修改字节码
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassReader cr = null;
String enhancedClassName = classSource.getEnhancedName();
try {
cr = new ClassReader(new FileInputStream(
classSource.getFile()));
} catch (IOException e) {
e.printStackTrace();
return null;
}
ClassVisitor cv = new EnhancedModifier(cw,
className.replace(".", "/"),
enhancedClassName.replace(".", "/"));
cr.accept(cv, 0);
2. 求问大神现在做android的hotfix用哪个框架比较好
一.基础知识1.阿里的热更新框架已经开源了。但已经很久没有更新过新版本了。当前的版本只支持到了Android4.4。由于5.0起新的ART虚拟机、更严格的SELinux策略以及对64位的支持之类的事,使得Xposed都在开发上做了很多调整。我不知道Dexposed现在是否支持,但至少阿里没有开源。2.在本地动态执行远端下发的代码是极度危险的行为。利用此方法执行非法代码等或用于绕过GooglePlay等市场的审查是违反相关协议的,也是对用户极度不负责任的行为。3.在一些访问非常密集的地方使用热更新可能会对效率产生相对比较大的影响,应该避免使用.4.我们可以对Java的ScriptEngine进行一些封装成为一个HotPatch类使得它更适合做热更新的工作。5.首先,检查热更新补丁的管道一定要建立在https上,因为下发代码是极其危险的,如果被劫持,后果是无法想象的。其次,请求时最好自动带上Android版本、手机型号、地区、版本号等信息,以方便更精确地下发,千万不能下发错。6.Java在运行时加载对应的类是通过ClassLoader来实现的,ClassLoader本身是一个抽象来,Android中使用PathClassLoader类作为Android的默认的类加载器7.我们的如果想做hotpatch,一定要保证我们的hotpacthdex文件出现在dexElements列表的前面。二.常用的热更新技术框架:基于 空间的HotFix→→要使用到androiddex分包方案→拆分dex的项目的话,可以参考一下谷歌的multidex方案实现.大众点评的NuWa←项目补丁自动化做的很完整alibaba/AndFix阿里巴巴的DexPoseddalvik_patch实现multidex使用React-Native实现app热部署的一次实践alibaba/AndFix三、常用的热更新技术框架比较AdvantagedisadavantageNuWa1,可以新增类和字段,2,兼容到6.0系统1,基本原理是classloader,类加载器2,不能修改资源文件,如图片布局等(可通过动态布局实现)AndFix1,支持Android2.3到6.0版本2,支持arm与x86系统架构3,支持dalvik和ART的runtime4,不需要重启App即可应用补丁1,不能新增类和字段,2,不能修改资源文件,3,不能修改manifest文件4,不能新增成员变量5,不能使用加固后的apk制作pacth文件四、github地址网络的同学的实现HotFix点评的同学的实现Nuwa阿里的同学的实现AndFix另:AndFix对static的支持不太好,下面是试验的Demo:添加了一个静态的字段addString:通过AndFix来制作patch会直接报错: