热补丁方案和Instant-Run总结

热补丁方案和Instant-Run总结

      Android的热补丁技术在今年一直很火爆,目前比较成熟的技术有淘宝的Dexposed、支付宝的Andfix、微信的DexdiffQZoneHotfix。去年一年react-native非常火爆,其中之一的优势就是可以热更新,客户端可以实时更新到最新代码,还好谷歌在Android Studio 2.0添加了instant-run的功能,可以实现热启动、温启动、冷启动,instant-run实则也是一种热补丁方案。下面我就针对超级热补丁和instant-run作了一些总结,因为DexposedAndfix使用都有一定局限性,而Dexdiff微信并没有开源,只是讲把旧的dex和补丁dex合并为一个新的dex,没有具体阐述实现方式,故这里采用Hotfix来作为研究对象。我水平有限,不正之处请指出。

一、热补丁方案(以qzone超级热补丁方案为例)原理

1、动态改变BaseDexClassLoader对象间接引用的dexElements

     Classloader加载类是从DexElements依次遍历dex,如果dex中有该类则返回,没有则遍历下一个dex,所以Hotfix的解决方式就是改变dexElementsdex的顺序,具体实现方式是通过反射的方式获取应用的PathdexClassloader>PathList>DexElements,再获取补丁dexDexClassloader>PathList>DexElements,然后通过combinArray的方法将2DexElements合并,补丁的DexElements放在前面,然后使用合并后的DexElements作为PathdexClassloader中的DexElements,这样在加载的时候就可以优先加载到补丁dex,从中可以加载到我们的补丁类。

2、在app打包的时候,阻止相关类去打上CLASS_ISPREVERIFIED标志:

     相关类之所以会被打上CLASS_ISPREVERIFIED,是因为类和引用它的类在同一个dex文件中,那么引用它的类就会被CLASS_ISPREVERIFIED标志,这样在采用分包方案时,假如类和引用它的类不在一个dex文件中,程序就会报错。解决办法就是在每个类的构造方法中通过javassist注入代码加载辅助类,辅助类是在一个单独的类,单独会被打包成一个dex文件。另外为了实现代码在执行dex命令前注入,需要在build.gradle中新建一个task,使其在执行dex前执行。

二、Instant-Run 原理

1、第一次编译apk

     通过transform apiinstant-run.jarinstant-run-bootstrap.jar打包到主dex中,然后会替换AndroidManifest.xml中的application配置,使用的是bootstrapapplication来代理我们的application,接着使用asm在每个类中添加$change字段,在每个方法前加入一段调用逻辑,最后把源代码编译成dex,然后存放到压缩包instant-run.zip中。

2、app运行期:

     bootstrapapplication提供了2个方法,attachBaseContextonCreate方法。在attachBaseContext方法中,首先是获取资源resource.ap_的路径,然后调用setupClassLoader方法将原有的BootClassLoader > PathClassLoader改为BootClassLoader > IncrementalClassLoader → PathClassLoader继承关系,接着调用createRealApplication创建app真实的application,并获取真实application的生命周期。而在onCreate方法中,依次执行monkeyPatchApplication(反射替换ActivityThread中的各种Application成员变量)、monkeyPatchExistingResource(反射替换所有存在的AssetManager对象),最后会判断一个Server是否启动,没有启动则启动,Socket接收patch列表,并调用realApplicationonCreate方法。

3、有代码修改时:

     首先生成对应的$override类,instant-run提供一个AppPatchesLoaderImpl类,记录修改的类列表,然后打包成patch,通过socket传递给appappserver接收到patch之后,分别按照handleColdSwapPatchhandleHotSwapPatchhandleResourcePatchpatch进行处理,restart使patch生效。

三、热补丁方案和Instant Run 区别

1、加载补丁dex方式:

1)超级热补丁方案加载补丁dex的思路是动态改变BaseDexClassLoader对象间接引用的dexElements,将补丁的dex插入到dexElements前面,这样就可以加载到补丁类了。

(2)Instant Run的思路使用instant-run.jargradle plugin会自动把instant-run.jar打到dex中,IncrementalClassLoader设置为默认PathClassLoaderparent,由于ClassLoader采用双亲委托模型,会先去parent查找类,而IncrementalClassLoader会在Application中去加载补丁dex,这样就可以加载补丁类了。

2、补丁类生成和调用:

1)热补丁方案补丁生成:项目每次编译后会记录所有class文件和so文件的MD5值,在编译新版本时会计算对比新本版和旧版本的MD5值,不同的类是补丁类,将补丁类打包成补丁。

2Instant Run的补丁生成以及调用:

     Instant Run的补丁类是由gradle plugin自动生成,并不是我们自己修改后的类,该类是实现了IncrementalChange接口的类。该类类名在原名后面添加$override,复制我们修改后类的方法,实现IncrementalChange 接口的access$dispatch方法,该方法会根据传递过来的方法签名调用本类的同名方法。使用我们的补丁类只要把原类的$change字段设置为该类,那就会调用该类的access$dispatch方法,就会使用修改后的方法了。

     AppPatchesLoaderImpl类记录了全部被修改的类。当收到补丁通知时,只需新建一个DexClassLoader,去反射加载补丁dex中的AppPatchesLoaderImpl类,调用load方法即可,load方法中会去加载全部补丁类,并赋值给对应原类的$change,这样就可以调用修改后的方法了。

3、启动方式

(1)超级热补丁方案需要重启应用:Hotfix方案中程序加载类的只有一个ClassLoader,即PathClassLoader,这样在加载过类后就不会重新加载类,进行补丁修复必须重新启动应用才可以。

(2)Instant-Run提供了三种方式:热启动(修改方法和变量值)、温启动(资源的修改)、冷启动(增加类、修改类的继承等),instant-run中加载类是基于多ClassLoader的,采用的是双亲委托模式,IncrementalClassLoaderPathClassLoaderparent,每次更新补丁,就会新建一个ClassLoader,实现类的重新加载。

4、资源的替换:

1)超级热补丁通过反射实现资源替换:使用反射调用addAssetPath方法,将补丁资源加载到AssetManager中,然后通过AssetManager来创建一个新的Resources对象,这样就可以访问到补丁资源了。

2Instant-Run资源替换:instant-run的替换资源核心逻辑是替换资源目录并且调用FileManager.writeAaptResources方法去操作resources.ap_文件,但writeAaptResources的源码暂时还没有看到。

 

四、超级热补丁和热补丁方案和Instant Run 优劣对比

1、超级热补丁的优势:

(1)字节码注入:Hotfix现在注入字节码都是使用javassist,方便维护,而instant-run在字节码注入方面使用的是ASM框架,维护成本比较高,不过个人感觉随着研究instant-run肯定会使用javassist,方便操作。

(2)混淆、dex处理:Hotfix因为应用相对比较广泛,大神们已经在混淆、dex上踩过较多坑,实践开发中较容易,而instant-run因为是刚公布不久,目前处在研究阶段,坑应该比较多。instant-run是基于transform api的,而所有的transform操作是由TransformManager管理的,也就是说它执行的时机是固定的,如果涉及到混淆、dex处理,instant run就不够灵活了,这些task的顺序都是不可变的。其是否可以像hotfix那样新建task,还需要具体实践,有待研究。

2、Instant-Run的优势

(1)更新补丁启动方式:instant-run根据补丁文件后缀提供了3种启动方式(热启动、温启动、冷启动)更加方便更新补丁,而不用像hotfix每次都重新启动,这一点优势大大的。

(2)Application打补丁:instant-run实现了application替换,使用代理application去执行应用的初始化工作,然后再加载一个真正的application,这样就可以实现application打补丁;Hotfixapplication因为要用来加载辅助类hock.dex,所以是不能打补丁的,要想打补丁必须间接打包,就是application中引用的方法放在单独一个类里,这样去修改这个类实现打补丁。

(3)资源的替换:Hotfix资源的替换使用的是反射,在性能上有一定消耗,而instant-run是资源目录的替换和操作resources.ap_文件来实现,对资源替换支持比较好。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值