美团点评Robust(泛型与热更新方案)

-- 两类热更新方案:

  Android平台出现了一些优秀的热更新方案,主要可以分为两类:一类是基于multidex的热更新框架,包括Nuwa、Tinker等;另一类就是native hook方案,如阿里开源的Andfix和Dexposed。
  1.基于native hook的方案:需要针对dalvik虚拟机和art虚拟机做适配,需要考虑指令集的兼容问题,需要native代码支持,兼容性上会有一定的影响;
  2.基于Multidex的方案,需要反射更改DexElements,改变Dex的加载顺序,这使得patch需要在下次启动时才能生效,实时性就受到了影响,同时这种方案在android N [speed-profile]编译模式下可能会有问题。

  Android N 7.0混合使用AOT编译,解释和JIT三种运行时。
  Google高调发布了Android Studio 2.0,其中最重要的新特性Instant Run,实现了对代码修改的实时生效(热插拔)。我们在了解Instant Run原理之后,实现了一个兼容性更强的热更新方案,这就是产品化的hotpatch框架--Robust。

> 泛型
  泛型是jdk1.5引入的。在jdk1.5以前,如果要实现类似泛型的功能,基本上都是依赖于Object。泛型的出现减少了很多强转的操作,同时避免了很多运行时的错误,在编译期完成检查。泛型,instanceof 。
  进一步想一下,泛型类型擦除到底都擦除了哪些信息,是全部擦除吗?
  其实java虚拟机规范中为了响应在泛型类中如何获取传入的参数化类型等问题,引入了signature,LocalVariableTypeTable等新的属性来记录泛型信息,所以所谓的泛型类型擦除,仅仅是对方法的code属性中的字节码进行擦除,而原数据中还是保留了泛型信息的,这些信息被保存在class字节码的常量池中,使用了泛型的代码调用处会生成一个signature签名字段,signature指明了这个常量在常量池的地址。
  java中的泛型都是编译器层面来完成的,在生成的java字节码中是不包含任何泛型中的类型信息的,使用泛型时加上的类型参数,会在编译时被编译器去掉。这个过程称为类型擦除。泛型是通过类型擦除来实现的,编译器在编译时擦除了所有泛型类型相关的信息,【所以在运行时不存在任何泛型类型相关的信息(暂且这么说,实际上并不是完全擦除).泛型擦除具体来说就是在编译成字节码时首先进行类型检查,接着进行类型擦除(即所有类型参数都用他们的限定类型替换,包括类、变量和方法)】。
  泛型类型参数不能是基本类型。泛型擦除会导致任何在运行时需要知道确切类型信息的操作都无法编译通过。虚拟机巧妙使用桥方法的方式,解决了类型擦除和多态的冲突。
  泛型中的通配符一般分为非限定通配符和限定通配符两种,限定通配符有两种: <? extends T>和 <? super T>。<? extends T> 保证泛型类型必须是 T 的子类来设定泛型类型的上边界,<? super T> 来保证泛型类型必须是 T 的父类来设定类型的下边界,泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。
-- 特征签名的概念就不一样了,java语言规范和java虚拟机规范中存在不同的定义。
 1.java语言层面的方法特征签名可以表述为:特征签名 = 方法名 + 参数类型 + 参数顺序;
 2.JVM层面的方法特征签名可以表述为:特征签名 = 方法名 + 参数类型 + 参数顺序 + 返回值类型;

  自动化做的事情就是根据修改bug后的代码生成最终可执行的dex,就目前来说,整个补丁制作流程包括:.java ->.class ->.dex ->.smali->.dex 。
  补丁的自动化过程中主要有这么两类问题:Java编译器的优化;ProGuard的优化。

> Android热更新方案Robust开源
Android热更新方案Robust开源,新增自动化补丁工具- https://github.com/Meituan-Dianping/Robust
Android热更新方案Robust- https://tech.meituan.com/android_robust.html
Android热更新方案Robust开源,新增自动化补丁工具- https://tech.meituan.com/android_autopatch.html
Android N混合编译与对热补丁影响解析- https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286341&idx=1&sn=054d595af6e824cbe4edd79427fc2706&scene=0#wechat_redirect

 Robust热更新系统的两大插件:插桩埋点以及补丁自动化。
 Robust热更新系统借鉴Instant Run原理,实现了一个兼容性更强而且实时生效的热更新方案。其基本思路是,Robust热更新系统在一个方法的入口处插入一段跳转代码,当发现某个方法出现bug就跳转执行补丁中的代码,略过原有代码的执行,否则执行原有方法体逻辑。
  Java编译器的优化工作包括Java编译器会自动生成一些桥方法以及移动代码的位置等,比较典型的就是泛型方法、内部类和Lambda表达式。补丁自动化的过程中使用注解来标注需要补丁的方法,所以当Java编译器针对泛型移动代码时,注解也会被移动,直接导致补丁上线后无法修复问题。以Java编译器对泛型方法的处理为例,Java编译器会为泛型方法生成一个桥方法(在桥方法里面调用真正的方法,桥方法的参数是object的类型,注意这类桥方法Robust热更新系统并没有对其插桩),同时Java编译器把原方法上的注解移动到桥方法上,针对泛型方法制作补丁时,就变成了针对泛型方法的桥方法制作补丁了。Lambda表达式也与此类似,编译器把Lambda表达式的内容,移到了一个新的方法(Java编译器为我们生成的access开头的方法)里面去,而且我们还无法给Lambda表达式加上注解。
  为了解决上述的问题,自动化提供了一个静态方法(Robust.modify()),支持在泛型或者Lambda表达式里面调用这个静态方法,自动化扫描所有的方法调用,检测到这个静态方法的调用就就可以找到找到需要制作补丁的方法。这样就可以避免由于Java编译器做的一些优化工作导致我们无法修复预期的bug。
  从ProGuard的工作流来看,ProGuard做的工作基本主要包含:压缩、优化、混淆以及最后的校验。体现到代码层面上做的事情就是:混淆类名、方法名、字段名,修改方法、字段访问性,删除方法(上例中内部类的构造方法),方法的内联,甚至是减少方法的参数(这就改变了方法签名)等等。大体可以总结为三大问题:混淆、优化、内联,其中优化相关操作,比如说改变方法签名和删除方法,我们可以把这类问题划归到内联,因为在优化后的代码里面这些方法和内联的方法一样,都消失了。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值