(一)Andfix热修复原理

别来无恙 提交于 2019-11-29 06:23:25

AndFix,全称是Android hot-fix。是阿里开源的一个Android热补丁框架,允许APP在不重新发布版本的情况下修复线上的bug。支持Android 2.3 到 7.0。

一、动态加载

热修复、热更新、插件化都是利用动态加载的原理(相关知识:类加载机制、虚拟机)

二、热修复

Andfix(最轻量级的热修复,只能改方法)Android热修复的先驱,通过动态加载dex文件,在native层进行方法替换实现热修复,局限性:兼容性问题,每个Android版本都需要维护一套Andfix的实现代码,维护麻烦,现该开源库已停止维护了。优点:即时生效。
Andfix principle

Tink dex替换,是在java层实现,不足:冷启动后生效

三、源码追踪

要通过方法替换实现热修复,那么就需要去了解一下底层的类加载机制,特别是方法加载这一块的具体实现流程。

大概流程就是先加载类,然后再将类中的methodfield加载到class中,具体如下:

1、env->FindClass jni函数中找到类的函数(方法在类里面,根据类加载找到方法加载的途径)

2、ClassLinker::FindClass 是env->FindClass的主要实现的函数。

3、DefineClass 是ClassLinker::FindClass 中返回的类的类型。

4、LoadClass 在DefineClass 中调用,此函数是加载类的主要函数。

5、LoadClassMembers 在LoadClass中调用,此函数是加载方法的函数。

6、LinkCode 在LoadClassMembers中调用,此函数是加载字节码的函数。

7、LinkMethod 在LinkCode中调用,此函数设置方法的执行入口(两种:SetEntryPointFromPortableCompiledCode 和 SetEntryPointFromQuickCompiledCode)。

8、NeedsInterpreter 在LinkCode中调用,此函数设置是否需要解释器。

9、UnregisterNative 在LinkCode中调用,此函数中设置了访问权限和入口。

10、UpdateMethodsCode 在LinkCode中调用,此函数刷新方法的字节码。

11、UpdateEntrypoints 在UpdateMethodsCode中调用,此函数更新方法入口。

四、源码分析:

这里只列出关键代码片段,想直接看源码的话可以点每个方法的链接进去网页看

/art/runtime/mirror/art_method.h

art_method 方法的结构体

592  struct PACKED(4) PtrSizedFields {
593    // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
594    // compiled code.解释器模式(JIT)方法入口指针
595    void* entry_point_from_interpreter_;
596
597    // Pointer to JNI function registered to this method, or a function to resolve the JNI function.jni方法入口指针
598    void* entry_point_from_jni_;
599
600    // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
601    // portable compiled code or the interpreter.快速模式,预编译模式AOT(ahead of time)AOT:Ahead Of Time,指在运行前编译,比如普通的静态编译 JIT:Just In Time,指在运行时编译,边运行边编译,比如java虚拟机在运行时就用到JIT技术 
602    void* entry_point_from_quick_compiled_code_;

/art/runtime/jni_internal.cc

FindClass (env->FindClass) 寻找类

589  static jclass FindClass(JNIEnv* env, const char* name) {
590    CHECK_NON_NULL_ARGUMENT(name);
591    Runtime* runtime = Runtime::Current();
592    ClassLinker* class_linker = runtime->GetClassLinker();//类的链接器
593    std::string descriptor(NormalizeJniClassDescriptor(name));
594    ScopedObjectAccess soa(env);
595    mirror::Class* c = nullptr;
596    if (runtime->IsStarted()) {//是否已经初始化完成
597      StackHandleScope<1> hs(soa.Self());
598      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
599      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
        //调用classLinker的findClass,(参数1:来自env,参数2:来自类名,参数3:类加载器)
600    } else {
601      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
602    }
603    return soa.AddLocalReference<jclass>(c);
604  }

/art/runtime/class_linker.cc

ClassLinker::FindClass FindClass的具体实现,根据返回的DefineClass继续追踪代码

//参数self:线程,参数descriptor:类名的标识,参数class_loader:类加载器
2117  mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
2118                                      Handle<mirror::ClassLoader> class_loader) {
  //此处省略code
  //双亲委托机制,类加载过就不会再加载
2128  // Find the class in the loaded classes table.
2129  mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
2130  if (klass != nullptr) {
2131    return EnsureResolved(self, descriptor, klass);
2132  }
2133  // Class is not yet loaded.
2134  if (descriptor[0] == '[') {
2135    return CreateArrayClass(self, descriptor, hash, class_loader);
2136  } else if (class_loader.Get() == nullptr) {//判断类加载器是否为空,这里是类加载器为空的处理
  //从系统启动类里找,若找到,返回一个DefineClass
2137    // The boot class loader, search the boot class path.
2138    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
2139    if (pair.second != nullptr) {
2140      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
2141                         *pair.second);
2142    } else {//报错,ClassNotFoundException
2146      mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
2147      self->SetException(ThrowLocation(), pre_allocated);
2148      return nullptr;
2149    }
2150  } else if (Runtime::Current()->UseCompileTimeClassPath()) {//这里是类加载器不为空的处理
2151    //此处省略code
2216}

/art/runtime/class_linker.cc

ClassLinker::DefineClass 对kclass进行赋值,其中LoadClass方法实现了类加载的主要功能。

2218mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
2219                                        Handle<mirror::ClassLoader> class_loader,
2220                                        const DexFile& dex_file,
2221                                        const DexFile::ClassDef& dex_class_def) {
  //此处省略code
2225  // Load the class from the dex file.
  //类未加载就对kclass进行赋值
2226  if (UNLIKELY(!init_done_)) {
2227    // finish up init of hand crafted class_roots_
2228    if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
2229      klass.Assign(GetClassRoot(kJavaLangObject));
2230    } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
2231     //此处省略code
2243  }
2244
2245  if (klass.Get() == nullptr) {
  //申请空间
2250    klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
//此处省略code
  //加载类
2257  LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
   //此处省略code
2320}

/art/runtime/class_linker.cc

ClassLinker::LoadClass 把dex文件的信息初始化后放到class中,其中LoadClassMembers方法将类的成员加载到类中

//DexFile:dex文件对象,DexFile::ClassDef:要加载的类在dex中的描述信息
2727  void ClassLinker::LoadClass(const DexFile& dex_file,
2728                            const DexFile::ClassDef& dex_class_def,
2729                            Handle<mirror::Class> klass,
2730                            mirror::ClassLoader* class_loader) {
  //初始化
2734  const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
2735  CHECK(descriptor != nullptr);
2736
2737  klass->SetClass(GetClassRoot(kJavaLangClass));
//此处省略kclass的一些赋值操作
2747	//拿到类的索引放入class中
2748  klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
//此处省略kclass的一些赋值操作
2757  OatFile::OatClass oat_class;
2758  if (Runtime::Current()->IsStarted()
2759      && !Runtime::Current()->UseCompileTimeClassPath()
2760      && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
2761    LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);//加载class的成员
2762  } else {
2763    LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
2764  }
2765}

/art/runtime/class_linker.cc

ClassLinker::LoadClassMembers 加载类的静态、实例成员变量和方法到类中,最后执行LinkCode方法,链接字节码

2767  void ClassLinker::LoadClassMembers(const DexFile& dex_file,
2768                                   const byte* class_data,
2769                                   Handle<mirror::Class> klass,
2770                                   mirror::ClassLoader* class_loader,
2771                                   const OatFile::OatClass* oat_class) {
2772  // Load fields.
2773  ClassDataItemIterator it(dex_file, class_data);
2774  Thread* self = Thread::Current();
  //静态成员变量
2775  if (it.NumStaticFields() != 0) {
  //ArtField
2776    mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
2777    if (UNLIKELY(statics == nullptr)) {
2778      CHECK(self->IsExceptionPending());  // OOME.
2779      return;
2780    }
  //添加到kclass
2781    klass->SetSFields(statics);
2782  }
  //实例成员变量
2783  if (it.NumInstanceFields() != 0) {
2784    mirror::ObjectArray<mirror::ArtField>* fields =
2785        AllocArtFieldArray(self, it.NumInstanceFields());
2786    if (UNLIKELY(fields == nullptr)) {
2787      CHECK(self->IsExceptionPending());  // OOME.
2788      return;
2789    }
2790    klass->SetIFields(fields);
2791  }
//此处省略kclass 的 filed 赋值操作
  
2813  // Load methods.
2814  if (it.NumDirectMethods() != 0) {
  //ArtMethod 
2816    mirror::ObjectArray<mirror::ArtMethod>* directs =
2817         AllocArtMethodArray(self, it.NumDirectMethods());
//此处省略kclass的一些method赋值操作
  //链接code字节码
2845    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
//此处省略code
2870}

/art/runtime/class_linker.cc

LinkCode 根据之前存入kclass的索引,找到方法,然后设置方法执行入口()

2627  void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
2628                           const DexFile& dex_file, uint32_t dex_method_index,
2629                           uint32_t method_index) {
//此处省略code
  //GetOatMethod 通过方法索引找到方法
2642    const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
  //LinkMethod
2643    oat_method.LinkMethod(method.Get());
2644  }
2645
2646  // Install entry point from interpreter.
  //NeedsInterpreter 是否需要解释器(native方法和代理方法没有dex字节码,不需要解释器执行)
 //设置方法执行入口,方式与LinkMethod中设置的方式相同EntryPointFromQuickCompiledCode 和 EntryPointFromPortableCompiledCode
2647  bool enter_interpreter = NeedsInterpreter(method.Get(),
2648                                            method->GetEntryPointFromQuickCompiledCode(),
2649#if defined(ART_USE_PORTABLE_COMPILER)
2650                                            method->GetEntryPointFromPortableCompiledCode());
//此处省略code
//UnregisterNative 函数中设置了访问权限和入口
2705    method->UnregisterNative(Thread::Current());
//此处省略code
  //将结构体数据赋值,并更新函数执行的入口 UpdateEntryPoints
2717  runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
2718                                                   method->GetEntryPointFromQuickCompiledCode(),
2719#if defined(ART_USE_PORTABLE_COMPILER)
2720                                                   method->GetEntryPointFromPortableCompiledCode(),
2721#else
2722                                                   nullptr,
2723#endif
2724                                                   have_portable_code);
2725}

/art/runtime/oat_file.cc

**OatFile::OatMethod::LinkMethod ** 设置两种方法执行入口

595void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
596  CHECK(method != NULL);
597#if defined(ART_USE_PORTABLE_COMPILER)
  //设置两种方法执行入口
598  method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
599#endif
600  method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
601}

/art/runtime/mirror/art_method.cc

ArtMethod::UnregisterNative 注册方法

353 void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) {
//此处省略code
358  if (is_fast) {
  //设置访问权限
359    SetAccessFlags(GetAccessFlags() | kAccFastNative);
360  }
  //设置方法的入口
361  SetEntryPointFromJni(native_method);
362}
363
  //UnregisterNative 函数内部调用了 RegisterNative
364void ArtMethod::UnregisterNative(Thread* self) {
365  CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
366  // restore stub to lookup native pointer via dlsym
367  RegisterNative(self, GetJniDlsymLookupStub(), false);
368}

/art/runtime/instrumentation.cc

Instrumentation::UpdateMethodsCode 刷新方法的字节码

679void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
680                                        const void* portable_code, bool have_portable_code) {
//此处省略code
  //更新方法入口
724  UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
725}

五、虚拟机的启动入口

虚拟机的入口函数:

/frameworks/base/core/jni/AndroidRuntime.cpp

AndroidRuntime::start

Android执行的第一个方法:

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[])

java虚拟机加载的第一个类是string,但是Android的入口方法在 ZygoteInit.java 类中

底层调用Android入口方法流程:

1、找类 ZygoteInit.java

2、找方法 public static void main(String argv[])

3、调用方法 public static void main(String argv[])

六、art_method结构体

art_method结构体用来存储java方法相关信息,所以我们只需要修改art_method结构体中相关信息和java方法入口指针即可实现方法的替换,即Andfix热修复。这是Andfix热修复的核心。

七、兼容性问题

兼容性问题原因:原理是修改art_method结构体,每个Android版本不一样,art_method都可能不一样,所以每个版本都要做个版本维护,兼容性差。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!