前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android中的热修复

Android中的热修复

作者头像
码农帮派
发布2021-02-25 11:52:02
1.9K1
发布2021-02-25 11:52:02
举报
文章被收录于专栏:码农帮派码农帮派

主流的热修复方案:

1. 底层替换 - AndFix

在运行时替换掉底层有Bug的方法的地址,将他们的指针指向修复之后的方法的内存地址,从而实现热修复的功能。

底层替换方案限制较多,实现难度大,难于控制,容易产生方法的野指针;时效性号,加载轻快。立即生效。

2. 类加载方案 - Tinker、QZone

利用Android中类加载机制中的dexElements,将修复之后的dex文件放置到dexElements前面,屏蔽掉有问题的dex文件的加载,从而实现热修复的功能。

类加载方案时效性较差,因为Java的双亲委派机制的原因,首次打开不会重复加载类,需要再次打开才能生效,修复范围广,实现简单,易于控制。

动态加载dex实现热修复

Android中的类加载是通过DexClassLoader,在DexClassLoader中使用DexpathList将所有的dex文件加载到dexElements数组中,在进行.class加载的时候,会从dexElements数组中查找当前.class属于那个dex,然后从该dex中加载.class。

如上图,我们将一个修复后的Class文件HotFixTest文件打包成一个patch.dex文件,在App启动的时候,动态的将其加载到dexElements的最开始位置,这样在App加载的时候就会优先的加载这个热修复的类,从而实现dex热修复。

实现这一方案需要三步:

  • 1. 通过反射机制获取ClassLoader的dexPathList对象,通过dexPathList对象我们才能够向dexElements中插入dex文件的Element对象;
  • 2. 拿到dexPathList对象之后,调用它的makeDexElement方法,将我们的HotFixTest的class转换成包含dex的element数组;
  • 3. 通过反射机制拿到dexPathList的dexElements数组,合并我们新加载进来的dex数组(带有Patch的elements),这里需要将Patch的dexElements方法前面,最后复制到dexPathList中。

Tinker热修复原理

热修复的实现过程:

  • 1. 使用bsdiff对新旧apk做差异化分析,获得差异化产物patch.apk补丁文件。如上图这一过程发生在准备补丁apk的过程中,补丁生成好之后,内部会包含java的class类classes.dex文件、so的打包文件library.so、补丁资源文件;
  • 2. 这一步发生在App中,App需要下载补丁文件,然后使用dexpath将下载到的补丁文件中的dex、so、res文件和基准文件做全量合并,dex、so文件会被合并成tinker_classN.apk,res资源文件会合并成resource.apk文件;
  • 3. 动态加载tinker_classN.apk进行dex插队,从而实现热修复功能,资源resource.apk通过反射机制,替换Application的Context中assetManager实现资源文件更新。

Tinker会创建一个TinkerClassLoader类加载器,在这个类加载器中尝试加载class(loadClass),要是TinkerClassLoader加载不到class文件,会将加载任务提交到PathClassLoader。

Tinker会通过反射机制替换掉Application中的ClassLoader,同样的也是利用反射机制替换掉Resource中的ClassLoader,从而接管App中类加载的工作。

Android在加载类的时候,会遍历pathList对象的dexElements数组,该数据中是apk下所有dex文件的信息,在开始加载class文件的时候,会遍历dexElements数组,查看class文件在那个dexElementItem中,从而加载该dexElementItem对象的dex文件。

Tinker通过反射机制拿到pathList对象,然后通过pathListField.get()方法拿到了pathList中原有的dexElements数组,然后将我们要替换的dex文件数组additionalClassPathEntries和原有的dex文件数组合并,下面代码中的expandFieldArray方法,这一个过程就是dex文件的动态插队的过程:

上面是Tinker替换class文件的过程,对于so的库,Tinker同样是通过反射机制拿到pathList对象的nativeLibraryDirectories数组,该数组中保存了App需要使用的so文件。Tinker在拿到这个数组之后,将本次要替换的lib库动态插入到nativeLibraryDirectories数组中。

在Android10上禁止了dex文件的动态加载,所以无法对基准版本和patch文件的dex进行合并优化,从而无法实现类替换的功能,Tinker针对Android10,通过反射机制PackageManagerService的registerDexMode方法,强制系统触发dex文件的合并优化。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农帮派 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档