ART的反射调用(一)-获取Class对象

典型的反射调用形式为:

private static void classForName() {
    try {
        // 获取 Class 对象
        Class<?> clz = Class.forName("com.xx.xx.XX");
       ...
    } catch (Exception e) {
        e.printStackTrace();

    }
}

1. Class.forName

public static Class<?> forName(String className)
                throws ClassNotFoundException {
    return forName(className, true, VMStack.getCallingClassLoader());
}

public static Class<?> forName(String name, boolean initialize,ClassLoader loader)
          throws ClassNotFoundException{
    if (loader == null) {
        loader = BootClassLoader.getInstance();
    }
    Class<?> result;
    try {
        result = classForName(name, initialize, loader);
    } catch (ClassNotFoundException e) {
        Throwable cause = e.getCause();
        if (cause instanceof LinkageError) {
            throw (LinkageError) cause;
        }
        throw e;
    }
    return result;
}

static native Class<?> classForName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException;

Class.forName(String)函数实际是封装调用另一个重载版本的Class.forName(String,boolean,ClassLoader), 在传入第三个参数时首先调用VMStack.getCallingClassLoader方法,这个方法从函数名中大致可以判断出它的作用是获取当前调用者ClassLoader,最后再调用本地方法classForName

1.1 VMStack.getCallingClassLoader

native public static ClassLoader getCallingClassLoader();

// 定义在art/runtime/native/dalvik_system_VMStack.cc
static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
  ScopedFastNativeObjectAccess soa(env);
  NthCallerVisitor visitor(soa.Self(), 2);
  visitor.WalkStack();
  if (UNLIKELY(visitor.caller == nullptr)) {
    // The caller is an attached native thread.
    return nullptr;
  }
  return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
}

这个方法的核心调用是visitor.WalkStack(),WalkStack()的实现在art/runtime/stack.cc,具体的实现这里先不深究(由于时间原因,这块内容还未理解透彻),不过从代码中可以大致看出WalkStack()方法是根据函数调用栈帧获取到当前调用者,继而获取对应的ClassLoader

2. Class_classForName

// "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
                                 jobject javaLoader) {
    ScopedFastNativeObjectAccess soa(env);
    ScopedUtfChars name(env, javaName);
    if (name.c_str() == nullptr) {
        return nullptr;
    }

    // 将com.xx.xx转换为com/xx/xx
    if (!IsValidBinaryClassName(name.c_str())) {
        soa.Self()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
                                       "Invalid name: %s", name.c_str());
        return nullptr;
    }

    std::string descriptor(DotToDescriptor(name.c_str()));
    StackHandleScope<2> hs(soa.Self());
    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
    //核心调用,调用ClassLinker::FindClass()
    Handle<mirror::Class> c(
        hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
    if (c.Get() == nullptr) {
        ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
        env->ExceptionClear();
        jthrowable cnfe = reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
                                                                      WellKnownClasses::java_lang_ClassNotFoundException_init,
                                                                      javaName, cause.get()));
        if (cnfe != nullptr) {
            // Make sure allocation didn't fail with an OOME.
            env->Throw(cnfe);
        }
        return nullptr;
    }
    if (initialize) {
        //确保类已经完成解析
        class_linker->EnsureInitialized(soa.Self(), c, true, true);
    }
    return soa.AddLocalReference<jclass>(c.Get());
}

从代码中可以看出核心调用是class_linker->FindClass(...)

3. ClassLinker::FindClass

mirror::Class* ClassLinker::FindClass(Thread* self,
                                      const char* descriptor,
                                      Handle<mirror::ClassLoader> class_loader) {
    ...
    self->AssertNoPendingException();
    if (descriptor[1] == '\0') {
        // 只有基础类型的descriptor是1个字符
        return FindPrimitiveClass(descriptor[0]);
    }

    const size_t hash = ComputeModifiedUtf8Hash(descriptor);
    // 在传入的ClassLoader的已加载表中查找类, [3.1]
    mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get());
    if (klass != nullptr) {
        //确保类已经被解析
        return EnsureResolved(self, descriptor, klass);
    }

    // 执行到这里,意味着没有找到类
    if (descriptor[0] == '[') {
        return CreateArrayClass(self, descriptor, hash, class_loader);
    } else if (class_loader.Get() == nullptr) {
        // 如果ClassLoader是nullptr,则在boot_class_path_中查找
        ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
        if (pair.second != nullptr) {
            return DefineClass(self,
                           descriptor,
                           hash,
                           ScopedNullHandle<mirror::ClassLoader>(),
                           *pair.first,
                           *pair.second);
        } else {
            //仍然没有找到的话,创建一个ClassNoDefFoundError
            mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
            self->SetException(pre_allocated);
            return nullptr;
        }
    } else {
        ScopedObjectAccessUnchecked soa(self);
        mirror::Class* cp_klass;
        //递归的在传入的ClassLoader以及它的各级parent中查找类, [3.2]
        if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {
            if (cp_klass != nullptr) {
              //找到,直接返回
              return cp_klass;
            }
        }
        //如果Runtime是编译器,则直接创建ClassNoDefFoundError,只有在dex2oat中创建的Runtime会是编译器
        if (Runtime::Current()->IsAotCompiler()) {
            mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
            self->SetException(pre_allocated);
            return nullptr;
        }

        //执行到意味着仍然没有找到相应的类
        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) {
                return nullptr;
            }
            //调用ClassLoader的loadClass在传入的ClassLoader中查找
            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) {
            // 抛出NullPointerException
            ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
                                                   class_name_string.c_str()).c_str());
            return nullptr;
        } else {
            // 终于找到
            return soa.Decode<mirror::Class*>(result.get());
        }
    }
    UNREACHABLE();
}

ClassLinker::FindClass的逻辑可以总结为:

  1. 如果要找的类是基础类型,则直接调用FindPrimitiveClass查找基础类型;如果不是则在传入的ClassLoader的已加载类表中查找类(首先会判断ClassLaoder是否为null,如果为null,就在boot_class_table_中查找,否则就在ClassLoader自己的ClassTable中查找),如果找到,确保类已经被解析并返回
  2. 如果在传入的ClassLoader的已加载类表中没有找到类,则首先判断ClassLoader是否为空,如果为空,在boot_class_path_中查找,如果不为空,则调用FindClassInPathClassLoader在传入的ClassLoader以及它的各级parent中查找类
  3. 如果仍然没有找到,则调用传入的ClassLoader的loadClass在传入的ClassLoader中查找,找到返回,否则抛出异常

3.1 ClassLinker::LookupClass

mirror::Class* ClassLinker::LookupClass(Thread* self,
                                        const char* descriptor,
                                        size_t hash,
                                        mirror::ClassLoader* class_loader) {
    {
        ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
        //获取ClassLoader对应的ClassTable,如果ClassLoader为null,则则返回boot_class_table_
        ClassTable* const class_table = ClassTableForClassLoader(class_loader);
        if (class_table != nullptr) {
            mirror::Class* result = class_table->Lookup(descriptor, hash);
            if (result != nullptr) {
                return result;
            }
        }
    }
    if (class_loader != nullptr || !dex_cache_boot_image_class_lookup_required_) {
        return nullptr;
    }

    //在Image Space中查找类,Image Space映射的是boot.art和boot.oat
    mirror::Class* result = LookupClassFromBootImage(descriptor);
    if (result != nullptr) {
        result = InsertClass(descriptor, result, hash);
    } else {
        constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
        if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
            AddBootImageClassesToClassTable();
        }
    }
    return result;
}

ClassTable* ClassLinker::ClassTableForClassLoader(mirror::ClassLoader* class_loader) {
    return class_loader == nullptr ? &boot_class_table_ : class_loader->GetClassTable();
}

3.2 ClassLinker::FindClassInPathClassLoader

bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                             Thread* self,
                                             const char* descriptor,
                                             size_t hash,
                                             Handle<mirror::ClassLoader> class_loader,
                                             mirror::Class** result) {
    //递归调用的终止条件
    //如果是BootClassLoader,在boot_class_path_中查找类,并完成类的解析
    if (IsBootClassLoader(soa, class_loader.Get())) {
        ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
        if (pair.second != nullptr) {
            mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
            if (klass != nullptr) {
              *result = EnsureResolved(self, descriptor, klass);
            } else {
              *result = DefineClass(self,descriptor,hash,
                                    ScopedNullHandle<mirror::ClassLoader>(),
                                    *pair.first,
                                    *pair.second);
            }
            if (*result == nullptr) {
              self->ClearException();
            }
        } else {
            *result = nullptr;
        }
        return true;
    }
    //递归调用的终止条件
    //确保传入的ClassLoader必须是PathClassLoader
    if (class_loader->GetClass() != soa.Decode<mirror::Class*>(
                        WellKnownClasses::dalvik_system_PathClassLoader)) {
      *result = nullptr;
      return false;
    }

    // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
    StackHandleScope<4> hs(self);
    Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
    //递归调用,在parent中查找,一般情况下,一个应用的PathClassLoader的parent层级为:
    //PathClassLoader(apk)-->PathClassLoader(system)->BootClassLoader
    //所以一般情况下,传入的ClassLoader的parent就是系统的PathClassLoader, 从而递归调用只会调用两次,即
    //h_parent第一次等于PathClassLoader(system),第二次h_parent等于BootClassLoader
    bool recursive_result = FindClassInPathClassLoader(soa, self, descriptor, hash, h_parent, result);

    if (!recursive_result) {
        // Something wrong up the chain.
        return false;
    }

    if (*result != nullptr) {
        // Found the class up the chain.
        return true;
    }

    //获取DexFile的mCookie变量,mCookie指向的是已加载的dex文件列表
    ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
    //获取DexPathList的dexElements
    ArtField* const dex_file_field =
        soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
    //获取PathClassLoader的pathList属性
    mirror::Object* dex_path_list =
        soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
        GetObject(class_loader.Get());

    if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
        mirror::Object* dex_elements_obj =
            soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
            GetObject(dex_path_list);

        if (dex_elements_obj != nullptr) {
            Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
                hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
            for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
                mirror::Object* element = dex_elements->GetWithoutChecks(i);
                if (element == nullptr) {
                    // Should never happen, fall back to java code to throw a NPE.
                    break;
                }
                mirror::Object* dex_file = dex_file_field->GetObject(element);
                if (dex_file != nullptr) {
                  //将mCookie转换为LongArray*类型
                  mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
                  if (long_array == nullptr) {
                      break;
                  }
                  int32_t long_array_size = long_array->GetLength();
                  for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
                      //将LongArray转换为DexFile*类型
                      const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
                          long_array->GetWithoutChecks(j)));
                      //查找ClassDef
                      const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
                      if (dex_class_def != nullptr) {
                          //解析类,加载属性,方法
                          mirror::Class* klass = DefineClass(self,
                                                             descriptor,
                                                             hash,
                                                             class_loader,
                                                             *cp_dex_file,
                                                             *dex_class_def);
                          if (klass == nullptr) {
                            self->ClearException();
                            return true;
                          }
                          *result = klass;
                          return true;
                      }
                  }
              }
          }
      }
      self->AssertNoPendingException();
    }

    // Result is still null from the parent call, no need to set it again...
    return true;
}

FindClassInPathClassLoader主要逻辑是递归的在传入的ClassLoader的各级parent中依次查找类(典型的双亲委派模式) , 一般情况下,一个应用的PathClassLoader的parent层级为:`PathClassLoader(apk)-->PathClassLoader(system)->BootClassLoader`所以一般情况下,传入的ClassLoader的parent就是系统的PathClassLoader, 从而递归调用只会调用两次,即h_parent第一次等于PathClassLoader(system),第二次h_parent等于BootClassLoader. 在双亲中如果找到类,则直接返回,如果没有找到,则依次获取几个重要的变量BaseDexClassLoader.pathList, DexPathList.dexElement, DexFile.mCookie,根据这几个变量在传入的ClassLoader所加载的dex文件列表中查找类

4. 总结

一般而言,反射获取一个类时会调用Class.forName(String),获取类的过程可以总结为以下几步:

  1. 根据函数调用栈帧获取调用者的ClassLoader(如果调用的是Class.forName(String, boolean, ClassLoader),就没有根据栈帧获取ClassLoader这一步)
  2. 每一个ClassLoader都有一个ClassTable,用来缓存已经加载的类.如果传入的ClassLoader为null,就在BootClassLoader对应的boot_class_table_中查找,如果ClassLaoder不为null,就在ClassLoader对应的ClassTable中查找;如果找到Class,确保Class已经解析后,返回,否则进入下一步
  3. 如果没有在缓存的ClassTable中找到类,首先判断传入的ClassLoader是否为null,如果是,就在boot_class_path_中查找,如果不是,则递归的依次在传入的ClassLoader各级parent以及自身当中查找类(除了BootClassLoader,其余parent以及自身ClassLoader都必须是PathClassLoader,查找类主要是通过在Java层的几个重要变量完成:BaseDexClassLoader.pathList, DexPathList.dexElement, DexFile.mCookie),如果找到则返回(查找的过程中,如果找到了类,会完成对类的解析过程),如果没找到则再进入下一步
  4. 以上几步都没有找到的话,再调用传入的ClassLoader的loadClass进行最后一次查找(loadClass的过程中如果找到类,也会完成对类的解析),找到则返回,没找到则抛出异常
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容