AndFix

Foo.bar()这种直接调用与反射调用Foo.class.getDeclaredMethod(“bar”).invoke(null) 有什么区别吗?这个问题后续再谈

private native Object invoke(Object receiver, Object[] args, boolean accessible)
        throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;

这个invoke是一个native方法,它的native实现在 art/runtime/native/java_lang_reflect_Method.cc 里面,这个jni方法最终调用了 art/runtime/reflection.cc 的 InvokeMethod方法:

object InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod,
                     jobject javaReceiver, jobject javaArgs, bool accessible) {
  // 略...
  mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod);
  mirror::Class* declaring_class = m->GetDeclaringClass();
}

AndFix的实现里面,也正是使用这个 FromReflectedMethod 方法拿到Java层Method对应native层的ArtMethod指针,然后执行替换的

首先简单介绍下ART上的方法调用原理(本文不讨论解释模式,所有entrypoint均指compiled_code_entry_point)。在ART中,每一个Java方法在虚拟机(注:ART与虚拟机虽有细微差别,但本文不作区分,两者含义相同,下同)内部都由一个ArtMethod对象表示(native层,实际上是一个C++对象),这个native 的 ArtMethod对象包含了此Java方法的所有信息,比如名字,参数类型,方法本身代码的入口地址(entrypoint)等;暂时放下trampoline以及interpreter和jit不谈,一个Java方法的执行非常简单:

  1. 想办法拿到这个Java方法所代表的ArtMethod对象
  2. 取出其entrypoint,然后跳转到此处开始执行

image

AndFix就是基于这个原理来做热修复的,Sophix 对这个方案做了一些改进,也即整体替换,不过原理上都一样。二者在替换方法之后把原方法直接丢弃,因此无法实现AOP


《深入探索Android热修复技术原理》–Sophix团队

两种情况不适用:

  1. 引起原有类中发生结构变化的修改
  2. 修复了非静态方法会被反射调用

参考

ART深度探索开篇:从Method Hook谈起

我为Dexposed续一秒——论ART上运行时 Method AOP实现

https://github.com/tiann/epic