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

在Android NDK中,我们可以使用JNI(Java Native Interface)来实现C/C++代码和Java代码的相互调用。 下面是一个简单的例子,展示了如何在NDK中调用Java层的方法。 首先,在Java层中创建一个类,并在其中声明一个需要被C/C++回调的方法: ```java public class MyCallback { public void processData(byte[] data) { // 处理数据的逻辑 } } ``` 然后,在C/C++代码中,我们需要使用JNI来获取Java层的MyCallback对象,并调用其processData方法。具体步骤如下: 1. 首先,需要在C/C++代码中引入JNI头文件: ```c++ #include <jni.h> ``` 2. 获取Java层的MyCallback对象: ```c++ JNIEnv* env; JavaVM* jvm; // 获取当前线程的JNIEnv指针 jvm->AttachCurrentThread(&env, NULL); // 获取MyCallback类 jclass myCallbackClass = env->FindClass("com/example/MyCallback"); // 获取MyCallback对象 jmethodID constructor = env->GetMethodID(myCallbackClass, "<init>", "()V"); jobject myCallbackObj = env->NewObject(myCallbackClass, constructor); ``` 3. 调用MyCallback对象的processData方法: ```c++ // 获取processData方法的ID jmethodID processDataMethod = env->GetMethodID(myCallbackClass, "processData", "([B)V"); // 构造byte[]对象 jbyteArray data = env->NewByteArray(size); env->SetByteArrayRegion(data, 0, size, (jbyte*)buf); // 调用processData方法 env->CallVoidMethod(myCallbackObj, processDataMethod, data); ``` 最后,记得在C/C++代码中释放JNI相关资源: ```c++ jvm->DetachCurrentThread(); env->DeleteLocalRef(myCallbackClass); env->DeleteLocalRef(myCallbackObj); env->DeleteLocalRef(data); ``` 以上就是在NDK中实现线程回调Java层方法的基本步骤。需要注意的是,在调用Java层方法时,需要使用JNIEnv指针。此外,如果在多线程环境下操作JNI,需要使用jvm->AttachCurrentThread()方法获取当前线程的JNIEnv指针。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值