关于Android APP在线热修复bug方案的调研(二)(MultiDex的原理分析---Nuwa)

通过上一篇的分析,要进行在线修复bug,锁定的两套开源项目是:AndFix与Nuwa.

上篇文章对AndFix的实现机制进行了分析。


也提到了AndFix对static的支持不太好,下面是试验的Demo:

添加了一个静态的字段addString:


通过AndFix来制作patch会直接报错:



而同样的Demo使用Nuwa来制作Patch是没有问题的,下面是运行的结果:



那么采用Multi dex来进行hot fix的原理是什么?

上一篇中也提到了Nuwa是采用的Muxdex的方案,那么什么时Multi dex呢?

网上搜索一下就有很多了,用它来作为hot fix的关键代码如下:

其中extraElements指向的是patch中的dex,original指向的是bug.apk中的dex,combined是这两者合并之后的dex列表。


上面的代码来记DynamicAPK,可见host fix的原理其实就是将补丁文件中的dex文件放到apk的前面。而做Plugin开发时,就是将dex放到apk的后面。

为什么这样做可以了呢?

来看看系统中DexClassLoader加载class的代码实现:

从这段代码可以看出,系统在加载class的时候会依次遍历dex列表,找到第一个满足的class就直接return回去了。

所以如果将补丁中的dex放到APK的前面,系统就会先加载补丁文件中的class文件,从而达到了在内存中替换掉APK class的效果,以此来fix bug。


MultiDex需要注意的:

默认class文件只能引用同一个dex中的class文件。因为在加载的时候如果检测到class没有引用其他dex中的class,就会加上CLASS_ISPREVERIFIED标志。

而在java源文件中,是不能直接引用dex文件的。

所以要防止被打上CLASS_ISPREVERIFIED,就只有在编译完成后修改class文件,让它引用其他dex中的文件。

具体参考http://zhuanlan.zhihu.com/magilu/20308548


携程的DynamicAPK说的支持hot fix,应该只是指加载dex的时候,可以将补丁中的dex放到apk的前面。

但关于如果制作补丁,如何往class中插入代码都没有,这才是hot fix的真正大头。

这样就说支持真的好么.........

话说回来既能Plugin开发,又能host fix,这样的框架应该才是理想的.....


基于上述的调研,最后将目标锁定在了Nuwa上。


下面是将Nuwa整合进我的project:

1.下载源码,编译成功。将Nuwa/nuwa/build/outputs/aar目录下的nuwa.jar放到我的libs目录下:

还要添加lib依赖哦:

dependencies {
...................
     compile files('libs/nuwa.jar')
....................
 }


2.在top level的build.gradle中添加gradle依赖:


3.在我的App module的build.gradle中使用nuwa 插件,添加如下代码:




需要注意的:

   我使用了provider,它会比Application先起来,所以需要使用excludeClass来将这些在Application中之前起来的class全部排除掉。

   也就是编译后不进行class注入,当然这样这几个文件也就无法进行hot fix了。不过这些很少的文件一般也不会动吧。

  否则就会发生class not found(因为第二个dex此时都还没有加载,而每个class中都有被插入使用第二个dex class的代码)。

E/AndroidRuntime(11948): java.lang.NoClassDefFoundError: cn.jiajixin.nuwa.Hack
E/AndroidRuntime(11948): 	at com.nq.mam.app.MAMApp$1.<init>(MAMApp.java:201)
E/AndroidRuntime(11948): 	at com.nq.mam.app.MAMApp.<init>(MAMApp.java:201)
E/AndroidRuntime(11948): 	at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(11948): 	at java.lang.Class.newInstance(Class.java:1208)
E/AndroidRuntime(11948): 	at android.app.Instrumentation.newApplication(Instrumentation.java:990)
E/AndroidRuntime(11948): 	at android.app.Instrumentation.newApplication(Instrumentation.java:975)
E/AndroidRuntime(11948): 	at android.app.LoadedApk.makeApplication(LoadedApk.java:504)
E/AndroidRuntime(11948): 	at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4367)
E/AndroidRuntime(11948): 	at android.app.ActivityThread.access$1600(ActivityThread.java:141)
E/AndroidRuntime(11948): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1273)
E/AndroidRuntime(11948): 	at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(11948): 	at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime(11948): 	at android.app.ActivityThread.main(ActivityThread.java:5072)
E/AndroidRuntime(11948): 	at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(11948): 	at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(11948): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime(11948): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
E/AndroidRuntime(11948): 	at dalvik.system.NativeStart.main(Native Method)
W/ActivityManager( 1218):   Force finishing activity com.nq.mdm/.activity.MDMSplashActivity


以上基本就是配置的全部过程了,还是比较简单。

编译出APP后,记得保存APP下build/outputs/nuwa目录哦,这里面记录了每个class文件的hash值。

另外建议制作补丁时,还是用和原来编译APP同样的环境,免得生成的hash有什么不对的。。

在制作补丁文件时会,会将新的APK中的class的hash与该文件比较,将不一样的class文件提取出来制作成补丁文件。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值