return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
}
JNI_Onload
函数是我们进行动态注册的宝地,在其中调用RegisterNatives
即可进行注册。具体的方法和细节留到"动态注册"章节阐述。
虚拟机中的native方法
通常而言,一个Java方法在虚拟机中可以找到两种执行方式,一种是解释执行,另一种是机器码执行。解释执行时,解释器会去寻找字节码的入口地址。而机器码执行时,虚拟机会去寻找机器指令的入口地址。考虑到每个Java方法在虚拟机中都由ArtMethod
对象表示,字节码的入口信息(间接)存在其data_
字段中,而机器码入口信息则存在entry_point_from_quick_compiled_code_
字段中。如下所示。
// Must be the last fields in the method.
struct PtrSizedFields {
// Depending on the method type, the data is
// - native method: pointer to the JNI function registered to this method
// or a function to resolve the JNI function,
// - resolution method: pointer to a function to resolve the method and
// the JNI function for @CriticalNative.
// - conflict method: ImtConflictTable,
// - abstract/interface method: the single-implementation if any,
// - proxy method: the original interface method or constructor,
// - other methods: during AOT the code item offset, at runtime a pointer
// to the code item.
void* data_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
// the interpreter.
void* entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;
不过对于native方法而言,它在Java世界中只有定义没有实现,因此不会有字节码信息。虽然不需要存储字节码的入口地址,但native方法在调用过程中却会多出一步。
- 首先进入一个跳板函数,其中会处理Java参数到Native参数的转换以及线程状态切换等过程。
- 在跳板函数内部调用Native世界中实现的JNI函数。
这样一来,不用存储字节码入口信息的data_
字段就可以用来存储JNI函数的入口地址了。而entry_point_from_quick_compiled_code_
中存储的就是跳板函数的入口地址。具体可参考ART视角 | 为什么调用Native方法可以进入C++世界。
静态注册
当我们不在J