Java 层的每个方法在 ART 中都对应着一个 ArtMethod 的结构体(包含 Java 方法的所有信息,包括执行入口、访问权限、所属类和代码执行地址等),只要把原方法的结构体内容替换为新的结构体内容,则在调用原来方法的时候,真正执行的指令是新方法的指令,就可以实现热修复。
在 art/runtime/art_method.h
文件中,定义了 ArtMethod 的结构体内容。
class ArtMethod FINAL {
/* … */
GcRootmirror::Class declaring_class_;
std::atomicstd::uint32_t access_flags_;
uint32_t dex_method_index_;
uint16_t method_index_;
uint16_t hotness_count_;
uint16_t imt_index_;
struct PtrSizedFields {
ArtMethod** dex_cache_resolved_methods_;
void* data_;
void* entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;
}
2.2.2. 修复方案
-
方法①:将待修复的 java 方法对应的 ArtMethod 结构体中的每个字段进行替换。
-
方法②:将待修复的 java 方法对应的 ArtMethod 结构体整个进行替换。
不同的框架采用了不同方案:
-
AndFix 采用方法①,不同版本和不同厂商 ArtMethod 可能不同,存在兼容问题,导致方法替换失败。
-
Sophix 采用方法②,不存在兼容问题 。
无论采用哪种方案,由于类加载后,类的结构和方法数量就已经固定了,因此该方案有以下不适场景:
-
增加或减少方法和字段的个数。
-
改变原有类的结构。
Sophix 结合了底层替换方案和类加载方案各自的优点,以底层替换方案为主,类加载方案为辅,在热部署无法使用的情况下,自动降级为冷部署。
在 Android Studio 2.0 版本上,支持了一个新特性 Instant Run,实现了对代码修改的实时生效(热插拔)。
采用 Instant Run 方案的主要是 Robust 和 Aceso。
2.3.1. Instant Run 原理
在第一次构建 Apk 时:
-
在每一个类中注入了一个
$change
的成员变量,它实现了IncrementalChange
接口。 -
在每一个方法的第一行,插入了一段判断执行逻辑。
public class TestActivity {
// 注入一个类型为IncrementalChange的成员
IncrementalChange localIncre