NDK env->FindClass源码解析

在NDK中,我们通过env->FindClass来查找一个Java类,接下来,来看一下FindClass内部逻辑。

env->FindClass实际会调用jni_internal.cc#FindClass
来看art/runtime/jni_internal.cc
在这里插入图片描述
再来看art/runtime/class_linker.cc
在这里插入图片描述在这里插入图片描述
可以看到,这里有个LookupClass,传入要加载的类的签名,hash值和classLoader,如果返回的kclass不为null,则说明之前已经加载过,会直接return (双亲委托机制)。
如果不为null, 来看2148行,class_loader如果获取为null,那么调用FindInClassPath,从系统的启动类里去找,然后返回DifineClass。
如果class_loader不为null,则也会调用FindInClassPath,返回DefineClass。

再来看DefineClass
在这里插入图片描述
来看这里的LoadClass

在这里插入图片描述
先来看下它的参数
dex_file:dex文件
dex_class_def:要加载的类,在dex文件里面的一些信息
kclass:加载完成的一个对象 (class)
class_loader:类加载器

通过dex_file.GetClassDescriptor(dex_class_def);获取class的描述
然后的一些代码,都是给Kclass进行赋值,
所以这个方法的作用就是将dex文件的信息,赋值给kclass
最后,会调用LoadClassMembers,来加载fields和methods,并赋值给kclass

可以看到注释,load fields,这里可以看到用到了ArtField
在这里插入图片描述
load methods
NumdirectMethods:直接方法 ,这里可以看到ArtMethod,后期会用到
NumVirtualMethods:虚方法
在这里插入图片描述
在这里插入图片描述
接着,再来看LinkCode,通过字面名称,知晓是来链接字节码的
method:ArtMethod
在这里插入图片描述
在这里插入图片描述
其中const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);,通过索引来找方法,然后调用oat_method.LinkMethod(method.Get()); ,通过oat_method给函数设置一个默认的入口
来看art/runtime/oat_file.cc
在这里插入图片描述
method是ArtMethod,可以看到,根据解释器模式和AOT模式,设置不同的参数,这里的参数就是 art/runtime/mirror art_method.h结构体中的字段

再往下看,bool enter_interpreter = NeedsInterpreter(),用来判断是否是解释器模式
在这里插入图片描述
其中,isNative:是否是Native方法,ProxyMethod是否是代理方法,这两个都没有字节码,就算是解释器模式,也不需要使用解释器执行。
再来往下看,LinkCode方法中,根据是否是解释器模式设置不同的方法执行入口
在这里插入图片描述
在往下看,如果是本地Native方法,那么会调用method->UnregisterNative(Thread::Current());
在这里插入图片描述
来看art/runtime/mirror/art_method.cc#UnregisterNative
setAccessFlags:设置访问权限
setEntryPointFromJni:设置artmethod中的entry_point_from_jni_,表明是JNI函数
在这里插入图片描述
在这里插入图片描述
在回来看art/runtime/class_linker.cc ,可以看到,这里调用了Instrumentation.updateMethodsCode
在这里插入图片描述
来看art/runtime/instrumentation.cc
在这里插入图片描述
这里来看updateEntryPoints
在这里插入图片描述
可以看到,这里是更改方法的执行入口

#if defined(ART_USE_PORTABLE_COMPILER)
  method->SetEntryPointFromPortableCompiledCode(portable_code);
#endif
  method->SetEntryPointFromQuickCompiledCode(quick_code);

然后,设置method相应的属性。(SetEntryPointFromInterpreter)

小结

env->FindClass的内部调用过程

  1. env->FindClass //寻找Java Class
  2. art/runtime/jni_internal.cc#FindClass() //实际调用此方法
  3. art/runtime/class_linker.cc#FindClass() //接着调用此方法
  4. art/runtime/class_linker.cc#DefineClass() //最终会返回DefineClass
  5. art/runtime/class_linker.cc#LoadClass() //DefineClass内部会调用LoadClass,将dex文件的信息,赋值给kclass
  6. art/runtime/class_linker.cc#LoadClassMembers() //调用LoadClassMembers,加载fields和methods,并赋值给kclass
  7. art/runtime/class_linker.cc#LinkCode() //链接字节码
  8. art/runtime/oat_file.cc#LinkMethod() //LinkCode内部会调用LinkMethod,通过oat_method给函数设置一个默认的入口
  9. art/runtime/class_linker.cc#NeedsInterpreter() //是否是解释器模式
  10. art/runtime/mirror/art_method.cc#UnregisterNative() //如果是Native方法会设置访问权限、设置artmethod中的entry_point_from_jni_(表明是JNI函数)
  11. art/runtime/instrumentation.cc#UpdateMethodsCode() //更新方法的执行入口
  12. art/runtime/instrumentation.cc#UpdateEntrypoints() //UpdateMethodsCode会调用此方法,真正修改方法的执行入口

其他

源码为Android 5.1.0_r3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章