Android Andfix热修复原理

AndFix 原理

AndFix 用来修复 Bug 的方法,核心是通过方法的替换。是在Native层实现的,支持即时生效,原理是通过修改ArtMethod结构体中的入口点,从而加载补丁包中修复后的方法。

具体原理就是把旧的方法的ArtMethod结构体所有的成员变量,全部替换,替换为我们新的方法的ArtMethod中的值。
新的方法,通过我们类加载器,加载Class,然后,通过Class反射,拿到我们的Method,拿到Method之后,就可以拿到ArtMethod。然后把就的ArtMethod挨个挨个地把字段全部替换成新的ArtMethod里的值。替换之后,我们的内存里面,保存的这个方法,它的结构体里面,比如说它的指向入口,指向新方法了。

底层的ArtMethod结构体:记录了方法所有的信息,(包括属于哪一个类,访问权限,代码执行地址等等)
在这里插入图片描述

而Tinker的核心是通过Dex完整的替换 (根据Multidex原理),是在Java层实现的,需要冷启动后生效,因为ClassLoader类加载器,采用的是双亲委托模式,因为我们的类Class被我们系统加载过之后,就不会再加载了,这是我们虚拟机的一个很显著的特性。

java的内存机制

在这里插入图片描述

方法区

当JVM使用类加载器定位class文件,并将其输入到内存中时,会提取class文件的类型信息,并将这些信息存储到方法区中。同时放入方法区中的还有该类型中的类静态变量。

堆内存

Java程序在运行时创建的所有类型对象和数组都存储在堆中。JVM会根据new指令在堆中开辟一个对象类型的对象内存空间。但是堆中开辟对象的空间并没有任何人工指令可以回收,而是通过JVM的垃圾回收器负责回收。

每启动一个线程,JVM都会为它分配一个Java栈。用于存放方法中的局部变量,操作数以及异常数据等。当线程调用每个方法时,JVM会根据方法区中该方法的字节码组件一个栈帧。并将该栈帧压入Java栈中,方法执行完毕时,JVM会弹出该栈帧并释放掉。

源码解析

来看 art/runtime/mirror art_method.h,可以看到有如下的属性
在这里插入图片描述
其中,我们需要特别关注的有
entry_point_from_interpreter_ //解释器模式(JIT)执行的入口
entry_point_from_jni_ //标记是否是JNI函数,如果是,指向指向入口
entry_point_from_quick_compiled_code_ //快速模式(AOT模式)的入口

根据解释器模式或AOT模式,会获取entry_point_from_interpreter_entry_point_from_quick_compiled_code_

AOT采用预编译的方式,字节码预编译成了机器码,程序运行的时候,就不需要实时去编译了。

那在android中,java代码怎么一步步被虚拟机所执行的 ?
java会先编译成.class字节码,然后被编译成dex字节码,然后在我们的虚拟机当中,就可以采用,解释器模式或者AOT模式,来执行我们的dex文件。

.class 是 java 虚拟机使用的可执行文件,.dex 是Android Dalvik和ART虚拟机的可执行文件。

OK,知道以上原理后,我们就可以通过替换entry_point_from_interpreter_entry_point_from_jni_entry_point_from_quick_compiled_code_,以及替换ArtMethod中的其他属性值,从而达到热修复的目的。

其他原理详见我的博客:
NDK env->FindClass源码解析
NDK env->GetStaticMethodID源码解析
Android虚拟机的启动 源码分析

手写AndFix

//把获得指向被替换的目标方法的指针(有bug的方法的指针)
art::mirror::ArtMethod *bugArtMethod = reinterpret_cast<art::mirror::ArtMethod *>(env->FromReflectedMethod(
        bugMethod));
//获得指向新的方法的指针(被修复了的方法的指针)
art::mirror::ArtMethod *fixArtMethod = reinterpret_cast<art::mirror::ArtMethod *>(env->FromReflectedMethod(
        fixMethod));
reinterpret_cast<art::mirror::Class *>(fixArtMethod->declaring_class_)->class_loader_ =
        reinterpret_cast<art::mirror::Class *>(bugArtMethod->declaring_class_)->class_loader_; //for plugin classloader
reinterpret_cast<art::mirror::Class *>(fixArtMethod->declaring_class_)->clinit_thread_id_ =
        reinterpret_cast<art::mirror::Class *>(bugArtMethod->declaring_class_)->clinit_thread_id_;
reinterpret_cast<art::mirror::Class *>(fixArtMethod->declaring_class_)->status_ =
        reinterpret_cast<art::mirror::Class *>(bugArtMethod->declaring_class_)->status_ - 1;
reinterpret_cast<art::mirror::Class *>(fixArtMethod->declaring_class_)->super_class_ = 0;
//把就函数的成员变量替换为新函数的
bugArtMethod->declaring_class_ = fixArtMethod->declaring_class_;
bugArtMethod->dex_cache_resolved_methods_ = fixArtMethod->dex_cache_resolved_methods_;
bugArtMethod->access_flags_ = fixArtMethod->access_flags_;
bugArtMethod->dex_cache_resolved_types_ = fixArtMethod->dex_cache_resolved_types_;
bugArtMethod->dex_code_item_offset_ = fixArtMethod->dex_code_item_offset_;
bugArtMethod->method_index_ = fixArtMethod->method_index_;
bugArtMethod->dex_method_index_ = fixArtMethod->dex_method_index_;

bugArtMethod->ptr_sized_fields_.entry_point_from_interpreter_ = fixArtMethod->ptr_sized_fields_.entry_point_from_interpreter_;
bugArtMethod->ptr_sized_fields_.entry_point_from_jni_ = fixArtMethod->ptr_sized_fields_.entry_point_from_jni_;
bugArtMethod->ptr_sized_fields_.entry_point_from_quick_compiled_code_ = fixArtMethod->ptr_sized_fields_.entry_point_from_quick_compiled_code_;

AndFix的兼容性问题

AndFix为什么会有兼容性问题

1.每个版本的ArtMethod都是不同的,需要对每个Android版本进行单独的维护。所以AndFix只维护到了Android 7.0,因为维护成本太高了。

2.因为Android是开源的,有些厂商会修改ArtMethod,比如多增加一个字段,而替换ArtMethod里的变量值的时候,有增减过字段等,指针地址会产生偏差,从而导致赋值失败,从而导致热修复失败。

如果解决AndFix的兼容性问题

既然替换字段会有问题,那就把整个AndFix都替换了。 --> AndFix的升级版本Sophix解决了这个问题,但是不开源。
关键在于sizeof(ArtMethod)的计算结果,我们需要在运行时动态获取当前设备虚拟机的ArtMethod结构体的大小。或者另一种说法是说对ArtMethod的成员变量大小进行了对齐处理

其他

AndFix Android7.0后,不再维护,推出sophix (收费)
Dalvik虚拟机没有ArtMethod,只有一个统一的执行函数,所以性能很差。在Dalvik虚拟机上的修复通过hook这个函数来实现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值