这是我参与「第四届青训营 」笔记创作活动的第6天
.apk文件
- AndroidMnifest.xml
- 文件均为二进制,合并了所有子项目的AndroidMnifest文件
- META-INF
- 签名相关文件,检验APK所有文件合法性,从而识别开发者,避免反编译
- classes.dex
- Android项目所有Java代码最终编译后的形式。Android是基于ART的,所以显示时运行的是对.class格式进行压缩合成的.dex格式文件
- resources.arsc
- 该文件包含所有资源id,具体id值和类型信息。
- R.xx类型的ID,实际都是由32位数字组成的资源ID,他们有不同的类型,包括字符串、数字、资源路径等。
- 资源路径实际内容(xml、图片信息)都是存放在res目录下的。
- 程序运行时,先从arcs文件中找到相应ID对应的资源路径,然后访问实际res文件中路径所在资源。
- assets
- 资源,不带资源ID的原始文件,可以直接指定路径访问该资源。
- 与arcs文件中的资源ID不同
- lib
- JNI相关的so库,它有armeabi、x86等库,apk会根据所安装Android设备的CPU的实际架构选择具体加载哪一个so库
- res
- 存放所有资源文件的目录
修复相关
1. 关于AndroidManifest的修复
关于注册单中出现的bug是无法修复的,系统会直接获取安装包中唯一AndroidManifest文件,在解析过程中不会访问补丁包信息。
加载新增组件:预先在安装包的注册清单买入代理的组件,每次新增组件时,进行替换,实现代理组件与系统进程中的通信。
2. 代码的修复
- 所有的Java代码最终都会编译为classes.dex格式文件,所以想要改变代码逻辑,需要在补丁包里包含一个新逻辑的dex文件,然后在程序运行时加载这个dex文件,并且改变执行流,从原安装包classes.dex文件引导到新的dex文件中去。
3. 资源的修复
主要修复资源包的内容,而资源包就是整个APK安装包,如果想要新增一个原有安装包里不存在的资源,就必须修改资源包的内容。
采取方法:吧原有的安装包替换为新的资源包or把新的资源包插入程序的查找过程中,而有些资源,比如桌面图图标、通知栏图标及RemoteView之类的资源,是由系统解析安装包里的资源得到的。**对于这类资源。任何热修复方案都无法进行资源的替换和修复。
4. so库的修复
so库的修复思路是最明确的。所有so库都是由System load进行加载,所以只要找到办法在加载时优先加载补丁包的so库,就能进行完整的底层代码替换了。
Sophix简单介绍
历史
- Dexposed 该框架方案对DVM过于依赖
- ANdfix、阿里百川Hotfix:可以兼容DVM/ART,但
- Andfix是由局限性的,其低层固定结构的替换方案稳定性不是很好
- 通过改造代码绕过限制来达到相同的修复目的,但是这种方式既不美观也不方便
- 只提供了代码层的修复,资源层和so库层的修复还未能实现
- Sophix 在Android热修复的三大领域:代码、资源、so库,以及安全性和易用性都做的很好。但不支持四个组件的增加
理念
核心设计理念:非侵入式。
- 不会侵入Apk的构建流程
- 唯一需要的是初始化和请求补丁两行代码
技术
代码修复
底层替换方案
缺点:底层替换的不稳定性
传统的底层替换方案,不论是Dexposed、Andfix还是其他,都是依赖直接修改虚拟机方法实体的具体字段实现的,如果开源代码中结构与商家设置的结构体结构不一致,通用性的替换机制就会出问题。(ArtMethod结构体的事情)
最终Sophix实现的是一种不修改底层具体结构的替换方案,不仅解决了兼容性问题,还忽略了ArtMethod结构的差异(某一框架的结构体结构),不需要兼容各个安卓版本;并且未来只要ArtMethod结构体是以线性结构排列,就可以直接使用将来的版本。
类加载方案
原理:App重新启动后让Classloader去加载新的类。 实际:在安卓系统上无法对一个类进行卸载的,在还没有运行到业务逻辑之前抢先加载补丁中的类,这样app重启后,补丁的类会被解析为新类,从而达到热修复目的。
Sophix,采用的是全量合成dex技术,该方案将直接利用Android原有的类查找和合成机制,快速合成新的全量dex文件。
- 优点: 不需要处理合成时方法数超过原有方法数的情况,也不会对dex的结构进行破坏性重构。
实际:Sophix重新编排了保重dex文件的顺序。当虚拟机查找类时,优先找到classes.dex中的类,然后是classes2.dex。。。也可以看做是dex文件级别的类插桩方案。这个方案对旧包与补丁包中的classes.dex的顺序进行了打破和重组,最终使系统自然地识别到这个顺序,达到了类覆盖的目的。这将会大大减少合成补丁的开销。
综上,Sophix的代码修复体系同时涵盖了两种方案。同时又对其进行了改进。
- 在补丁生成阶段,补丁工具会根据实际代码的变动情况自动选择:
- 针对小更改,在底层替换方案限制范围内,直接采用底层替换修复,这样做到了代码修复及时生效
- 超出底层替换限制的修改 会使用类加载替换,及时性较差,但可以达到热修复目的。
- 在运行阶段,Sophix会再次判断所运行的机型是否支持热修复,
- 补丁与机型底层虚拟机构造都支持热修复时,走底层替换修复方案
- 当机型底层虚拟机不支持热修复时会走类加载方案,从而达到良好的兼容效果。
资源修复
Sophix并没有直接参考Instant Run技术,而是构造一个package id为0x66的资源包,该包中只包含需要改变的资源项,然后直接在原有的AssetManager中通过addAssetPath()函数添加这个包就行了。
由于补丁包的 package id为0x66,不与目前已经加载的地址为 0x7f的包冲突,因此直接加载到已有的 AssetManger中就可以直接使用了。 补丁包里面的资源,只包含原有包里面没有的新增资源,以及原有的内容发生了改变的资源。并且sophix采用更加优雅的替换方式,直接在原有的 AssetManager对象进行解析和重构,这样所有原有对AssetManager对象的引用是没有发生改变的,所以就不需要向Instant Run那样进行繁琐的修改了。
Sophix的资源修复方案在性能上超过了Google官方的Instant Run方案,整个资源替换的方案优势如下:
- 不修改AssetManager的引用处,替换资源更快更完整
- 不必下载完整包,补丁包中只包含变动的资源
- 不需要在运行时合成完整包,不占用运行时计算和内容的资源
so库修复
so库的修复本质上是对native方法的修复和替换。 Sophix采用的是类似类修复反射注入方式,**将补丁so库的路径插入nativeLibraryDirectories数组的最前面,**加载so库时将会加载补丁的so库目录从而达到修复Bug的目的。