Andfix 原理分析


即時(shí)生效原理

app啟動(dòng)到一半的時(shí)候,所有需要發(fā)生變更的類已經(jīng)被加載過了,
在Android系統(tǒng)中是無法對(duì)一個(gè)已經(jīng)加載的類進(jìn)行卸載的。騰訊的Tinker的方案是讓ClassLoader去加載新的類,如果不重啟app,原有的類還在虛擬機(jī)中,就無法加載新的類。因此需要冷啟動(dòng)后,搶先加載修復(fù)補(bǔ)丁中的新類,從而達(dá)到熱修復(fù)的目的。

AndFix采用的方法是直接在已加載的類中的native層替換掉原方法,是在原有類的基礎(chǔ)上進(jìn)行修改的。

底層替換原理

每一個(gè) Java 方法在 Art 虛擬機(jī)中都對(duì)應(yīng)一個(gè) ArtMethodArtMethod 記錄了該方法的所有信息,包括所屬類、訪問權(quán)限、代碼執(zhí)行地址等。

通過env->FromReflectedMethod,可以由 Method 對(duì)象得到這個(gè)方法對(duì)應(yīng)的ArtMethod的真正起始地址,然后強(qiáng)轉(zhuǎn)為 ArtMethod 指針,通過指針的操作對(duì)其成員屬性進(jìn)行修改替換。

為什么這樣替換后就可以實(shí)現(xiàn)熱修復(fù)呢?
就需要先了解虛擬機(jī)方法調(diào)用的原理

虛擬機(jī)方法調(diào)用的原理

我的測(cè)試機(jī)為Android 5.1.1,所以我們以Android 5.1.1版本的源碼為例。

Android 5.1.1版本中的 ArtMethod 結(jié)構(gòu)體路徑為:
art/runtime/mirror/art_method.h#560。其中最重要的字段就是 entry_point_from_interpreter_entry_point_from_quick_compiled_code_ ,從名字可以看出它們就是方法執(zhí)行的入口。

Java代碼在Android中會(huì)被編譯成 Dex code,
Art虛擬機(jī)中可以采用解釋模式或AOT機(jī)器碼模式執(zhí)行Dex Code。

  • 解釋模式:就是取出Dex Code,逐條解釋執(zhí)行。如果方法的調(diào)用者是以解釋模式運(yùn)行的,調(diào)用該方法時(shí),就會(huì)獲取它的 entry_point_from_interpreter_ ,然后跳轉(zhuǎn)執(zhí)行。
  • AOT模式:就會(huì)預(yù)編譯Dex code對(duì)應(yīng)的機(jī)器碼,然后在運(yùn)行期間直接執(zhí)行機(jī)器碼,不需要逐條解釋執(zhí)行dex code。如果方法的調(diào)用者是以AOT機(jī)器碼方式執(zhí)行的,在調(diào)用該方法時(shí)就是跳轉(zhuǎn)到 entry_point_from_quick_compiled_code_ 中執(zhí)行的。

那是不是替換方法的執(zhí)行入口就可以了呢?
當(dāng)然不是,無論是解釋模式還是AOT機(jī)器碼模式,在運(yùn)行期間還會(huì)需要調(diào)用ArtMethod中的其他成員字段

源碼跟蹤驗(yàn)證

類加載流程

  1. FindClass (/art/runtime/jni_internal.cc#599)
  2. FindClass (/art/runtime/class_linker.cc#2117)
  3. DefineClass (/art/runtime/class_linker.cc#2218)
  4. LoadClass (/art/runtime/class_linker.cc#2727)
  5. LoadClassMembers (/art/runtime/class_linker.cc#2767)
  6. LinkCode (/art/runtime/class_linker.cc#2627)
  7. LinkMethod (/art/runtime/oat_file.cc#595)
  8. NeedsInterpreter (/art/runtime/class_linker.cc#2525)
  9. UnregisterNative(/art/runtime/mirror/art_method.cc#364)
  10. UpdateMethodsCode(/art/runtime/instrumentation.cc#679)
  11. UpdateEntrypoints(/art/runtime/instrumentation.cc#85)

JNI類的靜態(tài)成員函數(shù) FindClass

static jclass FindClass(JNIEnv* env, const char* name) {
    CHECK_NON_NULL_ARGUMENT(name);
    Runtime* runtime = Runtime::Current();
    ClassLinker* class_linker = runtime->GetClassLinker();
    std::string descriptor(NormalizeJniClassDescriptor(name));
    ScopedObjectAccess soa(env);
    mirror::Class* c = nullptr;
    if (runtime->IsStarted()) {
      StackHandleScope<1> hs(soa.Self());
      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
    } else {
      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
    }
    return soa.AddLocalReference<jclass>(c);
  }

在ART虛擬機(jī)進(jìn)程中,存在著一個(gè) Runtime 單例,用來描述ART運(yùn)行時(shí)。通過調(diào)用 Runtime 類的靜態(tài)成員函數(shù) Current 可以獲得上述Runtime單例。獲得了這個(gè)單例之后,就可以調(diào)用它的成員函數(shù) GetClassLinker 來獲得一個(gè) ClassLinker 對(duì)象。

ClassLinker 對(duì)象是在創(chuàng)建ART虛擬機(jī)的過程中創(chuàng)建的,用來加載類以及鏈接類方法

首先判斷ART運(yùn)行時(shí)是否已經(jīng)啟動(dòng)起來。如果已經(jīng)啟動(dòng),那么就通過調(diào)用函數(shù)GetClassLoader來獲得當(dāng)前線程所關(guān)聯(lián)的ClassLoader,并且以此為參數(shù),調(diào)用前面獲得的ClassLinker對(duì)象的成員函數(shù)FindClass來加載由參數(shù)name指定的類。

如果ART運(yùn)行時(shí)還沒有啟動(dòng),那么這時(shí)候只可以加載系統(tǒng)類。這個(gè)通過前面獲得的 ClassLinker 對(duì)象的成員函數(shù) FindSystemClass 來實(shí)現(xiàn)的。

ClassLinker類的成員函數(shù) FindClass

mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
                                      Handle<mirror::ClassLoader> class_loader) {
  DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
  DCHECK(self != nullptr);
  self->AssertNoPendingException();
  if (descriptor[1] == '\0') {
    // only the descriptors of primitive types should be 1 character long, also avoid class lookup
    // for primitive classes that aren't backed by dex files.
    return FindPrimitiveClass(descriptor[0]);
  }
  const size_t hash = ComputeModifiedUtf8Hash(descriptor);
  // Find the class in the loaded classes table.
  mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
  if (klass != nullptr) {
    return EnsureResolved(self, descriptor, klass);
  }
  // Class is not yet loaded.
  if (descriptor[0] == '[') {
    return CreateArrayClass(self, descriptor, hash, class_loader);
  } else if (class_loader.Get() == nullptr) {
    // The boot class loader, search the boot class path.
    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
    if (pair.second != nullptr) {
      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
                         *pair.second);
    } else {
      // The boot class loader is searched ahead of the application class loader, failures are
      // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
      // trigger the chaining with a proper stack trace.
      mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
      self->SetException(ThrowLocation(), pre_allocated);
      return nullptr;
    }
  } else if (Runtime::Current()->UseCompileTimeClassPath()) {
    // First try with the bootstrap class loader.
    if (class_loader.Get() != nullptr) {
      klass = LookupClass(descriptor, hash, nullptr);
      if (klass != nullptr) {
        return EnsureResolved(self, descriptor, klass);
      }
    }
    // If the lookup failed search the boot class path. We don't perform a recursive call to avoid
    // a NoClassDefFoundError being allocated.
    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
    if (pair.second != nullptr) {
      return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
                         *pair.second);
    }
    // Next try the compile time class path.
    const std::vector<const DexFile*>* class_path;
    {
      ScopedObjectAccessUnchecked soa(self);
      ScopedLocalRef<jobject> jclass_loader(soa.Env(),
                                            soa.AddLocalReference<jobject>(class_loader.Get()));
      class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
    }
    pair = FindInClassPath(descriptor, hash, *class_path);
    if (pair.second != nullptr) {
      return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
    }
  } else {
    ScopedObjectAccessUnchecked soa(self);
    mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
    if (klass != nullptr) {
      return klass;
    }
    ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                soa.AddLocalReference<jobject>(class_loader.Get()));
    std::string class_name_string(DescriptorToDot(descriptor));
    ScopedLocalRef<jobject> result(soa.Env(), nullptr);
    {
      ScopedThreadStateChange tsc(self, kNative);
      ScopedLocalRef<jobject> class_name_object(soa.Env(),
                                                soa.Env()->NewStringUTF(class_name_string.c_str()));
      if (class_name_object.get() == nullptr) {
        DCHECK(self->IsExceptionPending());  // OOME.
        return nullptr;
      }
      CHECK(class_loader_object.get() != nullptr);
      result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
                                               WellKnownClasses::java_lang_ClassLoader_loadClass,
                                               class_name_object.get()));
    }
    if (self->IsExceptionPending()) {
      // If the ClassLoader threw, pass that exception up.
      return nullptr;
    } else if (result.get() == nullptr) {
      // broken loader - throw NPE to be compatible with Dalvik
      ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
                                                      class_name_string.c_str()).c_str());
      return nullptr;
    } else {
      // success, return mirror::Class*
      return soa.Decode<mirror::Class*>(result.get());
    }
  }

  ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
  return nullptr;
}

參數(shù) descriptor 指向的是要加載的類的簽名,而參數(shù) class_loader 指向的是一個(gè)類加載器。

首先是調(diào)用另外一個(gè)成員函數(shù) LookupClass 來檢查參數(shù) descriptor 指定的類是否已經(jīng)被加載過。如果是的話,那么 ClassLinker 類的成員函數(shù) LookupClass 就會(huì)返回一個(gè)對(duì)應(yīng)的 Class 對(duì)象,這個(gè) Class 對(duì)象接著就會(huì)返回給調(diào)用者,表示加載已經(jīng)完成。

如果參數(shù) descriptor 指定的類還沒有被加載過,這時(shí)候主要就是要看參數(shù) class_loader 的值了。如果參數(shù) class_loader 的值等于NULL,那么就需要調(diào)用 FindInClassPath 來在系統(tǒng)啟動(dòng)類路徑尋找對(duì)應(yīng)的類。一旦尋找到,那么就會(huì)獲得包含目標(biāo)類的DEX文件,因此接下來就調(diào)用 ClassLinker 類的另外一個(gè)成員函數(shù) DefineClass 從獲得的DEX文件中加載參數(shù) descriptor 指定的類了。

知道了參數(shù) descriptor 指定的類定義在哪一個(gè)DEX文件之后,就可以通過 ClassLinker 類的另外一個(gè)成員函數(shù)
DefineClass 來從中加載它了。

ClassLinker類的成員函數(shù) DefineClass

mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
                                        Handle<mirror::ClassLoader> class_loader,
                                        const DexFile& dex_file,
                                        const DexFile::ClassDef& dex_class_def) {
  StackHandleScope<3> hs(self);
  auto klass = hs.NewHandle<mirror::Class>(nullptr);

  // Load the class from the dex file.
  if (UNLIKELY(!init_done_)) {
    // finish up init of hand crafted class_roots_
    if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangObject));
    } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangClass));
    } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangString));
    } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangRefReference));
    } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangDexCache));
    } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangReflectArtField));
    } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
      klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
    }
  }

  if (klass.Get() == nullptr) {
    // Allocate a class with the status of not ready.
    // Interface object should get the right size here. Regular class will
    // figure out the right size later and be replaced with one of the right
    // size when the class becomes resolved.
    klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
  }
  if (UNLIKELY(klass.Get() == nullptr)) {
    CHECK(self->IsExceptionPending());  // Expect an OOME.
    return nullptr;
  }
  klass->SetDexCache(FindDexCache(dex_file));
  LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
  ObjectLock<mirror::Class> lock(self, klass);
  if (self->IsExceptionPending()) {
    // An exception occured during load, set status to erroneous while holding klass' lock in case
    // notification is necessary.
    if (!klass->IsErroneous()) {
      klass->SetStatus(mirror::Class::kStatusError, self);
    }
    return nullptr;
  }
  klass->SetClinitThreadId(self->GetTid());

  // Add the newly loaded class to the loaded classes table.
  mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
  if (existing != nullptr) {
    // We failed to insert because we raced with another thread. Calling EnsureResolved may cause
    // this thread to block.
    return EnsureResolved(self, descriptor, existing);
  }

  // Finish loading (if necessary) by finding parents
  CHECK(!klass->IsLoaded());
  if (!LoadSuperAndInterfaces(klass, dex_file)) {
    // Loading failed.
    if (!klass->IsErroneous()) {
      klass->SetStatus(mirror::Class::kStatusError, self);
    }
    return nullptr;
  }
  CHECK(klass->IsLoaded());
  // Link the class (if necessary)
  CHECK(!klass->IsResolved());
  // TODO: Use fast jobjects?
  auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);

  mirror::Class* new_class = nullptr;
  if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
    // Linking failed.
    if (!klass->IsErroneous()) {
      klass->SetStatus(mirror::Class::kStatusError, self);
    }
    return nullptr;
  }
  self->AssertNoPendingException();
  CHECK(new_class != nullptr) << descriptor;
  CHECK(new_class->IsResolved()) << descriptor;

  Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));

  /*
   * We send CLASS_PREPARE events to the debugger from here.  The
   * definition of "preparation" is creating the static fields for a
   * class and initializing them to the standard default values, but not
   * executing any code (that comes later, during "initialization").
   *
   * We did the static preparation in LinkClass.
   *
   * The class has been prepared and resolved but possibly not yet verified
   * at this point.
   */
  Dbg::PostClassPrepare(new_class_h.Get());

  return new_class_h.Get();
}

ClassLinker類有一個(gè)類型為bool的成員變量 init_done_,用來表示ClassLinker是否已經(jīng)初始化完成。

如果ClassLinker正處于初始化過程,即其成員變量 init_done_ 的值等于false,并且參數(shù) descriptor 描述的是特定的內(nèi)部類,那么就將本地變量 klass 指向它們,其余情況則會(huì)通過成員函數(shù) AllocClass 為其分配存儲(chǔ)空間,以便后面通過成員函數(shù) LoadClass 進(jìn)行初始化。

ClassLinker類的成員函數(shù) LoadClass 用來從指定的DEX文件中加載指定的類。指定的類從DEX文件中加載完成后,需要通過另外一個(gè)成員函數(shù) InsertClass 添加到 ClassLinker 的已加載類列表中去。如果指定的類之前已經(jīng)加載過,即調(diào)用成員函數(shù)InsertClass得到的返回值不等于空,那么就說明有另外的一個(gè)線程也正在加載指定的類。這時(shí)候就需要調(diào)用成員函數(shù)EnsureResolved來保證(等待)該類已經(jīng)加載并且解析完成。另一方面,如果沒有其它線程加載指定的類,那么當(dāng)前線程從指定的DEX文件加載完成指定的類后,還需要調(diào)用成員函數(shù)LinkClass來對(duì)加載后的類進(jìn)行解析。最后,一個(gè)類型為Class的對(duì)象就可以返回給調(diào)用者了,用來表示一個(gè)已經(jīng)加載和解析完成的類。

ClassLinker類的成員函數(shù) LoadClass

void ClassLinker::LoadClass(const DexFile& dex_file,
                            const DexFile::ClassDef& dex_class_def,
                            Handle<mirror::Class> klass,
                            mirror::ClassLoader* class_loader) {
  CHECK(klass.Get() != nullptr);
  CHECK(klass->GetDexCache() != nullptr);
  CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
  const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
  CHECK(descriptor != nullptr);

  klass->SetClass(GetClassRoot(kJavaLangClass));
  if (kUseBakerOrBrooksReadBarrier) {
    klass->AssertReadBarrierPointer();
  }
  uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
  CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
  klass->SetAccessFlags(access_flags);
  klass->SetClassLoader(class_loader);
  DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
  klass->SetStatus(mirror::Class::kStatusIdx, nullptr);

  klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
  klass->SetDexTypeIndex(dex_class_def.class_idx_);
  CHECK(klass->GetDexCacheStrings() != nullptr);

  const byte* class_data = dex_file.GetClassData(dex_class_def);
  if (class_data == nullptr) {
    return;  // no fields or methods - for example a marker interface
  }

  OatFile::OatClass oat_class;
  if (Runtime::Current()->IsStarted()
      && !Runtime::Current()->UseCompileTimeClassPath()
      && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
    LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
  } else {
    LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
  }
}

dex_file: 類型為DexFile,描述要加載的類所在的DEX文件。
dex_class_def: 類型為ClassDef,描述要加載的類在DEX文件里面的信息。
klass: 類型為Class,描述加載完成的類。
class_loader: 類型為ClassLoader,描述所使用的類加載器。

LoadClass的任務(wù)就是要用dex_file、dex_class_def、class_loader三個(gè)參數(shù)包含的相關(guān)信息設(shè)置到參數(shù)klass描述的Class對(duì)象去,以便可以得到一個(gè)完整的已加載類信息。

關(guān)鍵:

  • setClassLoader:將class_loader描述的ClassLoader設(shè)置到klass描述的Class對(duì)象中,即給每一個(gè)已加載的類關(guān)聯(lián)一個(gè)類加載器。
  • SetDexClassDefIndex:通過DexFile的成員函數(shù)GetIndexForClassDef獲得正在加載的類在Dex文件中的索引號(hào),并設(shè)置到klass中

FindOatClass:從相應(yīng)的OAT文件中找到與正在加載的類對(duì)應(yīng)的一個(gè)OatClass結(jié)構(gòu)體oat_class。這需要利用到上面提到的DEX類索引號(hào),這是因?yàn)镈EX類和OAT類根據(jù)索引號(hào)存在一一對(duì)應(yīng)關(guān)系。

ClassLinker類的成員函數(shù) LoadClassMembers

void ClassLinker::LoadClassMembers(const DexFile& dex_file,
                                   const byte* class_data,
                                   Handle<mirror::Class> klass,
                                   mirror::ClassLoader* class_loader,
                                   const OatFile::OatClass* oat_class) {
  // Load fields.
  ClassDataItemIterator it(dex_file, class_data);
  Thread* self = Thread::Current();
  if (it.NumStaticFields() != 0) {
    mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
    if (UNLIKELY(statics == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetSFields(statics);
  }
  if (it.NumInstanceFields() != 0) {
    mirror::ObjectArray<mirror::ArtField>* fields =
        AllocArtFieldArray(self, it.NumInstanceFields());
    if (UNLIKELY(fields == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetIFields(fields);
  }
  for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self)));
    if (UNLIKELY(sfield.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetStaticField(i, sfield.Get());
    LoadField(dex_file, it, klass, sfield);
  }
  for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self)));
    if (UNLIKELY(ifield.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetInstanceField(i, ifield.Get());
    LoadField(dex_file, it, klass, ifield);
  }

  // Load methods.
  if (it.NumDirectMethods() != 0) {
    // TODO: append direct methods to class object
    mirror::ObjectArray<mirror::ArtMethod>* directs =
         AllocArtMethodArray(self, it.NumDirectMethods());
    if (UNLIKELY(directs == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetDirectMethods(directs);
  }
  if (it.NumVirtualMethods() != 0) {
    // TODO: append direct methods to class object
    mirror::ObjectArray<mirror::ArtMethod>* virtuals =
        AllocArtMethodArray(self, it.NumVirtualMethods());
    if (UNLIKELY(virtuals == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetVirtualMethods(virtuals);
  }
  size_t class_def_method_index = 0;
  uint32_t last_dex_method_index = DexFile::kDexNoIndex;
  size_t last_class_def_method_index = 0;
  for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
    if (UNLIKELY(method.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetDirectMethod(i, method.Get());
    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
    uint32_t it_method_index = it.GetMemberIndex();
    if (last_dex_method_index == it_method_index) {
      // duplicate case
      method->SetMethodIndex(last_class_def_method_index);
    } else {
      method->SetMethodIndex(class_def_method_index);
      last_dex_method_index = it_method_index;
      last_class_def_method_index = class_def_method_index;
    }
    class_def_method_index++;
  }
  for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
    StackHandleScope<1> hs(self);
    Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
    if (UNLIKELY(method.Get() == nullptr)) {
      CHECK(self->IsExceptionPending());  // OOME.
      return;
    }
    klass->SetVirtualMethod(i, method.Get());
    DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
    class_def_method_index++;
  }
  DCHECK(!it.HasNext());
}

從參數(shù)dex_file描述的DEX文件中獲得正在加載的類的靜態(tài)成員變量和實(shí)例成員變量個(gè)數(shù),并且為每一個(gè)靜態(tài)成員變量和實(shí)例成員變量都分配一個(gè)ArtField對(duì)象,接著通過ClassLinker類的成員函數(shù)LoadField對(duì)這些ArtField對(duì)象進(jìn)行初始化。初始化得到的ArtField對(duì)象全部保存在klass描述的Class對(duì)象中。

從參數(shù)dex_file描述的DEX文件中獲得正在加載的類的直接成員函數(shù)和虛擬成員函數(shù)個(gè)數(shù),并且為每一個(gè)直接成員函數(shù)和虛擬成員函數(shù)都分配一個(gè)ArtMethod對(duì)象,接著通過ClassLinker類的成員函數(shù)LoadMethod對(duì)這些ArtMethod對(duì)象進(jìn)行初始化。初始好得到的ArtMethod對(duì)象全部保存在klass描述的Class對(duì)象中。

參數(shù)klass描述的Class對(duì)象包含了一系列的ArtField對(duì)象和ArtMethod對(duì)象,其中,ArtField對(duì)象用來描述成員變量信息,而ArtMethod用來描述成員函數(shù)信息。

接下來繼續(xù)分析LinkCode函數(shù)的實(shí)現(xiàn),以便可以了解如何在一個(gè)OAT文件中找到一個(gè)DEX類方法的本地機(jī)器指令。

ClassLinker類的成員函數(shù) LinkCode

void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
                           const DexFile& dex_file, uint32_t dex_method_index,
                           uint32_t method_index) {
  if (Runtime::Current()->IsCompiler()) {
    // The following code only applies to a non-compiler runtime.
    return;
  }
  // Method shouldn't have already been linked.
  DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
  DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
#endif
  if (oat_class != nullptr) {
    // Every kind of method should at least get an invoke stub from the oat_method.
    // non-abstract methods also get their code pointers.
    const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
    oat_method.LinkMethod(method.Get());
  }

  // Install entry point from interpreter.
  bool enter_interpreter = NeedsInterpreter(method.Get(),
                                            method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
                                            method->GetEntryPointFromPortableCompiledCode());
#else
                                            nullptr);
#endif
  if (enter_interpreter && !method->IsNative()) {
    method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
  } else {
    method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
  }

  if (method->IsAbstract()) {
    method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
    method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
    return;
  }

  bool have_portable_code = false;
  if (method->IsStatic() && !method->IsConstructor()) {
    // For static methods excluding the class initializer, install the trampoline.
    // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
    // after initializing class (see ClassLinker::InitializeClass method).
    method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
    method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline());
#endif
  } else if (enter_interpreter) {
    if (!method->IsNative()) {
      // Set entry point from compiled code if there's no code or in interpreter only mode.
      method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
      method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
    } else {
      method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
      method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
    }
#if defined(ART_USE_PORTABLE_COMPILER)
  } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
    DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
    have_portable_code = true;
    method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
#endif
  } else {
    DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
  }

  if (method->IsNative()) {
    // Unregistering restores the dlsym lookup stub.
    method->UnregisterNative(Thread::Current());

    if (enter_interpreter) {
      // We have a native method here without code. Then it should have either the GenericJni
      // trampoline as entrypoint (non-static), or the Resolution trampoline (static).
      DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline()
          || method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline());
    }
  }

  // Allow instrumentation its chance to hijack code.
  Runtime* runtime = Runtime::Current();
  runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
                                                   method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
                                                   method->GetEntryPointFromPortableCompiledCode(),
#else
                                                   nullptr,
#endif
                                                   have_portable_code);
}
  • 參數(shù)method表示要設(shè)置本地機(jī)器指令的類方法。
  • 參數(shù)oat_class表示類方法method在OAT文件中對(duì)應(yīng)的OatClass結(jié)構(gòu)體。
  • 參數(shù)dex_file表示在dex文件中對(duì)應(yīng)的DexFile。
  • 參數(shù)dex_method_index表示DexFile中方法method的索引號(hào)。
  • 參數(shù)method_index表示類方法method的索引號(hào)。

通過參數(shù) method_index 描述的索引號(hào)可以在 oat_class 表示的OatClass結(jié)構(gòu)體中找到一個(gè) OatMethod 結(jié)構(gòu)體 oat_method。這個(gè) OatMethod 結(jié)構(gòu)描述了類方法method的本地機(jī)器指令相關(guān)信息,通過調(diào)用它的成員函數(shù)LinkMethod可以將這些信息設(shè)置到參數(shù)method描述的 ArtMethod 對(duì)象中去。

OatMethod 的成員函數(shù) LinkMethod

void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
  CHECK(method != NULL);
#if defined(ART_USE_PORTABLE_COMPILER)
  method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
#endif
  method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}

通過OatMethod類的成員函數(shù) GetPortableCode 和 GetQuickCode 獲得 OatMethod 結(jié)構(gòu)體中的 code_offset_ 字段,并且通過調(diào)用 ArtMethod 類的成員函數(shù) SetEntryPointFromCompiledCode 設(shè)置到參數(shù)method描述的 ArtMethod 對(duì)象中去。OatMethod 結(jié)構(gòu)體中的 code_offset_ 字段指向的是一個(gè)本地機(jī)器指令函數(shù),這個(gè)本地機(jī)器指令函數(shù)正是通過翻譯參數(shù)method描述的類方法的DEX字節(jié)碼得到的。

OatMethod 的 GetPortableCode 函數(shù)
const void* GetPortableCode() const {
      // TODO: encode whether code is portable/quick in flags within OatMethod.
      if (kUsePortableCompiler) {
        return GetOatPointer<const void*>(code_offset_);
      } else {
        return nullptr;
      }
    }
OatMethod 的 GetQuickCode 函數(shù)
const void* GetQuickCode() const {
      if (kUsePortableCompiler) {
        return nullptr;
      } else {
        return GetOatPointer<const void*>(code_offset_);
      }
    }

ClassLinker類的全局函數(shù) NeedsInterpreter

// Returns true if the method must run with interpreter, false otherwise.
static bool NeedsInterpreter(
    mirror::ArtMethod* method, const void* quick_code, const void* portable_code)
    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  if ((quick_code == nullptr) && (portable_code == nullptr)) {
    // No code: need interpreter.
    // May return true for native code, in the case of generic JNI
    // DCHECK(!method->IsNative());
    return true;
  }
#ifdef ART_SEA_IR_MODE
  ScopedObjectAccess soa(Thread::Current());
  if (std::string::npos != PrettyMethod(method).find("fibonacci")) {
    LOG(INFO) << "Found " << PrettyMethod(method);
    return false;
  }
#endif
  // If interpreter mode is enabled, every method (except native and proxy) must
  // be run with interpreter.
  return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&
         !method->IsNative() && !method->IsProxyMethod();
}

檢查參數(shù)method描述的類方法是否需要通過解釋器執(zhí)行。

在以下兩種情況下,一個(gè)類方法需要通過解釋器來執(zhí)行:

  1. 沒有對(duì)應(yīng)的本地機(jī)器指令,即參數(shù)quick_code和portable_code的值等于NULL。
  2. ART虛擬機(jī)運(yùn)行在解釋模式中,并且類方法不是JNI方法,并且也不是代理方法。

因?yàn)镴NI方法是沒有對(duì)應(yīng)的DEX字節(jié)碼的,因此即使ART虛擬機(jī)運(yùn)行在解釋模式中,JNI方法也不能通過解釋器來執(zhí)行。至于代理方法,由于是動(dòng)態(tài)生成的(沒有對(duì)應(yīng)的DEX字節(jié)碼),因此即使ART虛擬機(jī)運(yùn)行在解釋模式中,它們也不通過解釋器來執(zhí)行。

調(diào)用Runtime類的靜態(tài)成員函數(shù)Current獲得的是描述ART運(yùn)行時(shí)的一個(gè)Runtime對(duì)象。調(diào)用這個(gè)Runtime對(duì)象的成員函數(shù)GetInstrumentation獲得的是一個(gè)Instrumentation對(duì)象。這個(gè)Instrumentation對(duì)象是用來調(diào)試ART運(yùn)行時(shí)的,通過調(diào)用它的成員函數(shù)InterpretOnly可以知道ART虛擬機(jī)是否運(yùn)行在解釋模式中。

回到ClassLinker類的成員函數(shù) LinkCode

如果調(diào)用函數(shù)NeedsInterpreter得到的返回值enter_interpreter等于true,而且不是Native方法,那么就意味著參數(shù)method描述的類方法需要通過解釋器來執(zhí)行,這時(shí)候就將函數(shù)artInterpreterToInterpreterBridge設(shè)置為解釋器執(zhí)行該類方法的入口點(diǎn)。否則的話,就將另外一個(gè)函數(shù)artInterpreterToCompiledCodeBridge設(shè)置為解釋器執(zhí)行該類方法的入口點(diǎn)。

為什么我們需要為類方法設(shè)置解釋器入口點(diǎn)呢?根據(jù)前面的分析可以知道,在ART虛擬機(jī)中,并不是所有的類方法都是有對(duì)應(yīng)的本地機(jī)器指令的,并且即使一個(gè)類方法有對(duì)應(yīng)的本地機(jī)器指令,當(dāng)ART虛擬機(jī)以解釋模式運(yùn)行時(shí),它也需要通過解釋器來執(zhí)行。當(dāng)以解釋器執(zhí)行的類方法在執(zhí)行的過程中調(diào)用了其它的類方法時(shí),解釋器就需要進(jìn)一步知道被調(diào)用的類方法是應(yīng)用以解釋方式執(zhí)行,還是本地機(jī)器指令方法執(zhí)行。為了能夠進(jìn)行統(tǒng)一處理,就給每一個(gè)類方法都設(shè)置一個(gè)解釋器入口點(diǎn)。需要通過解釋執(zhí)行的類方法的解釋器入口點(diǎn)函數(shù)是artInterpreterToInterpreterBridge,它會(huì)繼續(xù)通過解釋器來執(zhí)行該類方法。需要通過本地機(jī)器指令執(zhí)行的類方法的解釋器入口點(diǎn)函數(shù)是artInterpreterToCompiledCodeBridge,它會(huì)間接地調(diào)用該類方法的本地機(jī)器指令。

判斷method是否是一個(gè)抽象方法。抽象方法聲明類中是沒有實(shí)現(xiàn)的,必須要由子類實(shí)現(xiàn)。因此抽象方法在聲明類中是沒有對(duì)應(yīng)的本地機(jī)器指令的,它們必須要通過解釋器來執(zhí)行。不過,為了能夠進(jìn)行統(tǒng)一處理,我們?nèi)匀患傺b抽象方法有對(duì)應(yīng)的本地機(jī)器指令函數(shù),只不過這個(gè)本地機(jī)器指令函數(shù)被設(shè)置為 GetQuickToInterpreterBridge。當(dāng)函數(shù) GetQuickToInterpreterBridge,就會(huì)自動(dòng)進(jìn)入到解釋器中去。

當(dāng)method是一個(gè)非類靜態(tài)初始化函數(shù)(class initializer)的靜態(tài)方法時(shí),我們不能直接執(zhí)行翻譯其DEX字節(jié)碼得到的本地機(jī)器指令。這是因?yàn)轭愳o態(tài)方法可以在不創(chuàng)建類對(duì)象的前提下執(zhí)行。這意味著一個(gè)類靜態(tài)方法在執(zhí)行的時(shí)候,對(duì)應(yīng)的類可能還沒有初始化好。這時(shí)候我們就需要先將對(duì)應(yīng)的類初始化好,再執(zhí)行相應(yīng)的靜態(tài)方法。為了能夠做到這一點(diǎn)。我們就先調(diào)用GetResolutionTrampoline函數(shù)得到一個(gè)Tampoline函數(shù),接著將這個(gè)Trampoline函數(shù)作為靜態(tài)方法的本地機(jī)器指令。這樣如果類靜態(tài)方法在對(duì)應(yīng)的類初始化前被調(diào)用,就會(huì)觸發(fā)上述的Trampoline函數(shù)被執(zhí)行。而當(dāng)上述Trampoline函數(shù)執(zhí)行時(shí),它們先初始化好對(duì)應(yīng)的類,再調(diào)用原來的類靜態(tài)方法對(duì)應(yīng)的本地機(jī)器指令。按照代碼中的注釋,當(dāng)一個(gè)類初始化完成之后,就可以調(diào)用函數(shù)ClassLinker::FixupStaticTrampolines來修復(fù)該類的靜態(tài)成員函數(shù)的本地機(jī)器指令,也是通過翻譯DEX字節(jié)碼得到的本地機(jī)器指令。這里需要注意的是,為什么類靜態(tài)初始化函數(shù)不需要按照其它的類靜態(tài)方法一樣設(shè)置Tampoline函數(shù)呢?這是因?yàn)轭愳o態(tài)初始化函數(shù)是一定保證是在類初始化過程中執(zhí)行的。

當(dāng)method需要通過解釋器執(zhí)行時(shí),那么當(dāng)該類方法執(zhí)行時(shí),就不能執(zhí)行它的本地機(jī)器指令,因此我們就先調(diào)用GetCompiledCodeToInterpreterBridge函數(shù)獲得一個(gè)橋接函數(shù),并且將這個(gè)橋接函數(shù)假裝為類方法的本地機(jī)器指令。一旦該橋接函數(shù)被執(zhí)行,它就會(huì)入到解釋器去執(zhí)行類方法。通過這種方式,我們就可以以統(tǒng)一的方法來調(diào)用解釋執(zhí)行和本地機(jī)器指令執(zhí)行的類方法。

判斷method是否是一個(gè)JNI方法。如果是的話,那么就調(diào)用ArtMethod類的成員函數(shù)UnregisterNative來初始化它的JNI方法調(diào)用接口。

ArtMethod類的成員函數(shù) UnregisterNative

void ArtMethod::UnregisterNative(Thread* self) {
  CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
  // restore stub to lookup native pointer via dlsym
  RegisterNative(self, GetJniDlsymLookupStub(), false);
}

UnregisterNative實(shí)際上就是將一個(gè)JNI方法的初始化入口設(shè)置為通過調(diào)用函數(shù)GetJniDlsymLookupStub獲得的一個(gè)Stub。這個(gè)Stub的作用是,當(dāng)一個(gè)JNI方法被調(diào)用時(shí),如果還沒有顯示地注冊(cè)有Native函數(shù),那么它就會(huì)自動(dòng)從已加載的SO文件查找是否存在一個(gè)對(duì)應(yīng)的Native函數(shù)。如果存在的話,就將它注冊(cè)為JNI方法的Native函數(shù),并且執(zhí)行它。這就是隱式的JNI方法注冊(cè)。

回到ClassLinker類的成員函數(shù) LinkCode

最后調(diào)用Instrumentation類的成員函數(shù)UpdateMethodsCode檢查是否要進(jìn)一步修改參數(shù)method描述的類方法的本地機(jī)器指令入口。

void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
                                        const void* portable_code, bool have_portable_code) {
  const void* new_portable_code;
  const void* new_quick_code;
  bool new_have_portable_code;
  if (LIKELY(!instrumentation_stubs_installed_)) {
    new_portable_code = portable_code;
    new_quick_code = quick_code;
    new_have_portable_code = have_portable_code;
  } else {
    if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
#if defined(ART_USE_PORTABLE_COMPILER)
      new_portable_code = GetPortableToInterpreterBridge();
#else
      new_portable_code = portable_code;
#endif
      new_quick_code = GetQuickToInterpreterBridge();
      new_have_portable_code = false;
    } else {
      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
      if (quick_code == class_linker->GetQuickResolutionTrampoline() ||
          quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
          quick_code == GetQuickToInterpreterBridge()) {
#if defined(ART_USE_PORTABLE_COMPILER)
        DCHECK((portable_code == class_linker->GetPortableResolutionTrampoline()) ||
               (portable_code == GetPortableToInterpreterBridge()));
#endif
        new_portable_code = portable_code;
        new_quick_code = quick_code;
        new_have_portable_code = have_portable_code;
      } else if (entry_exit_stubs_installed_) {
        new_quick_code = GetQuickInstrumentationEntryPoint();
#if defined(ART_USE_PORTABLE_COMPILER)
        new_portable_code = GetPortableToInterpreterBridge();
#else
        new_portable_code = portable_code;
#endif
        new_have_portable_code = false;
      } else {
        new_portable_code = portable_code;
        new_quick_code = quick_code;
        new_have_portable_code = have_portable_code;
      }
    }
  }
  UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
}

Instrumentation類是用來調(diào)用ART運(yùn)行時(shí)的。例如,當(dāng)我們需要監(jiān)控類方法的調(diào)用時(shí),就可以往Instrumentation注冊(cè)一些Listener。這樣當(dāng)類方法調(diào)用時(shí),這些注冊(cè)的Listener就會(huì)得到回調(diào)。當(dāng)Instrumentation注冊(cè)有相應(yīng)的Listener時(shí),它的成員變量instrumentation_stubs_installed_的值就會(huì)等于true。

總結(jié)
通過上述源碼跟蹤分析,一個(gè)類的加載過程完成了。加載完成后得到的是一個(gè)Class對(duì)象。這個(gè)Class對(duì)象關(guān)聯(lián)有一系列的 ArtField 對(duì)象和 ArtMethod 對(duì)象。其中,ArtField對(duì)象描述的是成員變量,而ArtMethod對(duì)象描述的是成員函數(shù)。對(duì)于每一個(gè)ArtMethod對(duì)象,它都有一個(gè)解釋器入口點(diǎn)和一個(gè)本地機(jī)器指令入口點(diǎn)。這樣,無論一個(gè)類方法是通過解釋器執(zhí)行,還是直接以本地機(jī)器指令執(zhí)行,我們都可以以統(tǒng)一的方式來進(jìn)行調(diào)用。同時(shí),理解了上述的類加載過程后,我們就可以知道,我們?cè)贜ative層通過JNI接口FindClass查找或者加載類時(shí),得到的一個(gè)不透明的jclass值,實(shí)際上指向的是一個(gè)Class對(duì)象。

類方法查找流程

  1. GetStaticMethodID (/art/runtime/jni_internal.cc#943)
  2. FindMethodID (/art/runtime/jni_internal.cc#140)

JNI類的靜態(tài)成員函數(shù) FindClass

static jmethodID GetStaticMethodID(JNIEnv* env, jclass java_class, const char* name,
                                     const char* sig) {
    CHECK_NON_NULL_ARGUMENT(java_class);
    CHECK_NON_NULL_ARGUMENT(name);
    CHECK_NON_NULL_ARGUMENT(sig);
    ScopedObjectAccess soa(env);
    return FindMethodID(soa, java_class, name, sig, true);
}

參數(shù) name 和 sig 描述的分別是要查找的類方法的名稱和簽名,而參數(shù) java_class 是對(duì)應(yīng)的類。參數(shù) java_class 的類型是jclass,從前面類加載過程的分析可以知道,它實(shí)際上指向的是一個(gè)Class對(duì)象。

JNI類的靜態(tài)成員函數(shù) FindMethodID

static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
                              const char* name, const char* sig, bool is_static)
    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(jni_class));
  if (c == nullptr) {
    return nullptr;
  }
  mirror::ArtMethod* method = nullptr;
  if (is_static) {
    method = c->FindDirectMethod(name, sig);
  } else if (c->IsInterface()) {
    method = c->FindInterfaceMethod(name, sig);
  } else {
    method = c->FindVirtualMethod(name, sig);
    if (method == nullptr) {
      // No virtual method matching the signature.  Search declared
      // private methods and constructors.
      method = c->FindDeclaredDirectMethod(name, sig);
    }
  }
  if (method == nullptr || method->IsStatic() != is_static) {
    ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
    return nullptr;
  }
  return soa.EncodeMethod(method);
}

執(zhí)行過程:

  1. 將參數(shù)jni_class的值轉(zhuǎn)換為一個(gè)Class指針c,因此就可以得到一個(gè)Class對(duì)象,并且通過ClassLinker類的成員函數(shù)EnsureInitialized確保該Class對(duì)象描述的類已經(jīng)初始化。
  2. Class對(duì)象c描述的類在加載的過程中,經(jīng)過解析已經(jīng)關(guān)聯(lián)上一系列的成員函數(shù)。這些成員函數(shù)可以分為兩類:Direct和Virtual。Direct類的成員函數(shù)包括所有的靜態(tài)成員函數(shù)、私有成員函數(shù)和構(gòu)造函數(shù),而Virtual則包括所有的虛成員函數(shù)。
  3. 經(jīng)過前面的查找過程,如果都不能在Class對(duì)象c描述的類中找到與參數(shù)name和sig對(duì)應(yīng)的成員函數(shù),那么就拋出一個(gè)NoSuchMethodError異常。否則的話,就將查找得到的ArtMethod對(duì)象封裝成一個(gè)jmethodID值返回給調(diào)用者。

我們通過調(diào)用JNI接口GetStaticMethodID獲得的不透明jmethodID值指向的實(shí)際上是一個(gè)ArtMethod對(duì)象。
當(dāng)我們獲得了一個(gè)ArtMethod對(duì)象之后,就可以輕松地得到它的本地機(jī)器指令入口,進(jìn)而對(duì)它進(jìn)行執(zhí)行。

總結(jié)
當(dāng)我們把舊方法(ArtMethod)的所有成員字段都替換為新方法(ArtMethod)的成員字段后,執(zhí)行時(shí)所有的數(shù)據(jù)就可以保持和新方法的數(shù)據(jù)一致。這樣在所有執(zhí)行到舊方法的地方,會(huì)獲取新方法的執(zhí)行入口、所屬類型、方法索引號(hào)、以及所屬dex信息,然后像調(diào)用舊方法一樣,執(zhí)行新方法的邏輯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容