[关闭]
@chopsticks 2014-12-21T17:38:51.000000Z 字数 23268 阅读 3886

JNI注册与使用

Android JNI


This article learns a lot from Laoluo's Blog. Thanks for his outstanding analysis!


1 Native Method Registeration

Native Functions can be registered or directly invoked as Native Method in 2 ways:

Most of following content are explaining the procedure of Native Function Registeration; The second subsection

1.1 Manually Register Native Functions

Manually Register Native Functions in SharedLibrary's JNI_OnLoad function

Structures and Definition in Native Function Registeration

JNINativeMethod

Location:dalvik/libnativehelper/include/nativehelper/jni.h

A structure used to describe the native methods

  1. typedef struct {
  2. const char* name;
  3. const char* signature; //the combined string describing types of parameters and return vuale
  4. void* fnPtr;
  5. } JNINativeMethod;
JNIEnv

Location:dalvik/libnativehelper/include/nativehelper/jni.h

JNIEnvhas a memeber named functions who points to a callback function table.
Dalvik虚拟机在创建一个JNIEnvExt结构体的时候,会将它的成员变量funcTable指向全局变量gNativeInterface所描述的一个回调函数表。

  1. struct _JNIEnv {
  2. /* do not rename this; it does not seem to be entirely opaque */
  3. const struct JNINativeInterface* functions;
  4. #if defined(__cplusplus)
  5. jint GetVersion()
  6. { return functions->GetVersion(this); }
  7. ...
JNINativeInterface

location:dalvik/vm/Jni.cpp

jniRegisterNativeMethods

Location:dalvik/libnativehelper/jnihelp.cpp

Before this function is called, a possible call-path can be the following:

Created with Raphaël 2.1.2System.loadlibrarySystem.loadlibraryRuntime.loadLibraryRuntime.loadLibraryRuntime.nativeloadRuntime.nativeloadDalvik_java_langRuntime_nativeloadDalvik_java_langRuntime_nativeloaddvmLoadNativeCodedvmLoadNativeCode

to be continued:

Created with Raphaël 2.1.2dvmLoadNativeCodedvmLoadNativeCodeaddSharedLibEntryaddSharedLibEntryNativeLibraryJNI_OnLoadNativeLibraryJNI_OnLoadjniRegisterNativeMethodsjniRegisterNativeMethods

Finally, jniRegisterNativeMethods is called for performing the real native methods registeration

  1. int jniRegisterNativeMethods(C_JNIEnv* env,
  2. const char* className, // aa/b/c
  3. const JNINativeMethod* gMethods, // list of all Native Mehtods
  4. int numMethods) // number of native methods

Either manually register or automatically register, jniRegisterNativeMethods will be invoked.
Two things are performed in this function:1) verify the validation of class name; 2) invoke JNIEnv->RegisterNatives to perform the realy registeration procedure.

  1. extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
  2. const JNINativeMethod* gMethods, int numMethods)
  3. {
  4. JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
  5. scoped_local_ref<jclass> c(env, findClass(env, className));
  6. if (c.get() == NULL) {
  7. //"Native registration unable to find class '%s'; aborting...", className);
  8. ...
  9. }
  10. if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
  11. //"RegisterNatives failed for '%s'; aborting...", className);
  12. ...
  13. }
  14. return 0;
  15. }
Manually Registeration in JNI_OnLoad
Automatically Registeration

JNIEnv->RegisterNatives

Location:dalvik/libnativehelper/include/nativehelper/jni.h

It is defined as follows:

  1. jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
  2. jint nMethods)
  3. { return functions->RegisterNatives(this, clazz, methods, nMethods); }

In Laoluo's post, he said:

    从前面Dalvik虚拟机的运行过程分析[1]一文可以知道,结构体JNIEnv的成员变量functions指向的是一个函数表,这个函数表又包含了一系列的函数指针,指向了在当前进程中运行的Dalvik虚拟机中定义的函数。对于结构体JNIEnv的成员函数RegisterNatives来说,它就是通过调用这个函数表中名称为RegisterNatives的函数指针来注册参数gMethods所描述的JNI方法的。


   从前面Dalvik虚拟机的启动过程分析[2]一文可以知道,上述函数表中名称为RegisterNatives的函数指针指向的是在Dalvik虚拟机内部定义的函数RegisterNatives,因此,接下来我们就继续分析它的实现

functions->RegisterNatives

Location:dalvik/vm/Jni.c

In this function, it reterives the class's object by invoking dvmDecodeIndirectRef firstly; then in the for statement, it registers each method in the methods[] by invoking dvmRegisterJNIMethod.

  1. /** Register one or more native functions in one class.
  2. * This can be called multiple times on the same method, allowing the
  3. * caller to redefine the method implementation at will.
  4. */
  5. static jint RegisterNatives(JNIEnv* env, jclass jclazz,
  6. const JNINativeMethod* methods, jint nMethods)
  7. {
  8. ScopedJniThreadState ts(env);
  9. ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
  10. ...
  11. for (int i = 0; i < nMethods; i++) {
  12. if (!dvmRegisterJNIMethod(clazz, methods[i].name,
  13. methods[i].signature, methods[i].fnPtr))
  14. { return JNI_ERR; }
  15. }
  16. return JNI_OK;
  17. }

dvmRegisterJNIMethod

Location:dalvik/vm/Jni.cpp

  1. 确保参数clazz所描述的类有一个名称为methodName的成员函数。首先是调用函数dvmFindDirectMethodByDescriptor来检查methodName是否是clazz的一个非虚成员函数,然后再调用函数dvmFindVirtualMethodByDescriptor来检查methodName是否是clazz的一个虚成员函数。

  2. 确保类clazz的成员函数methodName确实是声明为JNI方法,即带有native修饰符,这是通过调用函数dvmIsNativeMethod来实现的。

通过了前面的第1个检查之后,就可以获得一个Method对象method,用来描述要注册的JNI方法所对应的Java类成员函数。当一个Method对象method描述的是一个JNI方法的时候,它的成员变量nativeFunc保存的就是该JNI方法的地址,但是在对应的JNI方法注册进来之前,该成员变量的值被统一设置为dvmResolveNativeMethod。因此,当我们调用了一个未注册的JNI方法时,实际上执行的是函数dvmResolveNativeMethod。函数dvmResolveNativeMethod此时会在Dalvik虚拟内部以及当前所有已经加载的共享库中检查是否存在对应的JNI方法。如果不存在,那么它就会抛出一个类型为java.lang.UnsatisfiedLinkError的异常。

    注意,一个JNI方法是可以重复注册的,无论如何,函数dvmRegisterJNIMethod都是调用另外一个函数dvmUseJNIBridge来继续执行注册JNI的操作。
  1. /** Register a method that uses JNI calling conventions. */
  2. static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
  3. const char* signature, void* fnPtr)
  4. {
  5. if (fnPtr == NULL) { return false; }
  6. // If a signature starts with a '!', we take that as a sign that the native code doesn't need the extra JNI arguments (the JNIEnv* and the jclass).
  7. bool fastJni = false;
  8. if (*signature == '!') {
  9. fastJni = true; ++signature;
  10. ALOGV("fast JNI method %s.%s:%s detected", clazz->descriptor, methodName, signature);
  11. }
  12. Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
  13. if (method == NULL) {
  14. method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
  15. }
  16. if (method == NULL) {
  17. dumpCandidateMethods(clazz, methodName, signature);
  18. throwNoSuchMethodError(clazz, methodName, signature, "static or non-static");
  19. return false;
  20. }
  21. if (!dvmIsNativeMethod(method)) {
  22. ALOGW("Unable to register: not native: %s.%s:%s", clazz->descriptor, methodName, signature);
  23. throwNoSuchMethodError(clazz, methodName, signature, "native");
  24. return false;
  25. }
  26. if (fastJni) {
  27. // In this case, we have extra constraints to check...
  28. if (dvmIsSynchronizedMethod(method)) {
  29. // Synchronization is usually provided by the JNI bridge,
  30. // but we won't have one.
  31. ALOGE("fast JNI method %s.%s:%s cannot be synchronized",
  32. clazz->descriptor, methodName, signature);
  33. return false;
  34. }
  35. if (!dvmIsStaticMethod(method)) {
  36. // There's no real reason for this constraint, but since we won't
  37. // be supplying a JNIEnv* or a jobject 'this', you're effectively
  38. // static anyway, so it seems clearer to say so.
  39. ALOGE("fast JNI method %s.%s:%s cannot be non-static",
  40. clazz->descriptor, methodName, signature);
  41. return false;
  42. }
  43. }
  44. if (method->nativeFunc != dvmResolveNativeMethod) {
  45. /* this is allowed, but unusual */
  46. ALOGV("Note: %s.%s:%s was already registered", clazz->descriptor, methodName, signature);
  47. }
  48. method->fastJni = fastJni;
  49. dvmUseJNIBridge(method, fnPtr);
  50. return true;
  51. }

dvmUseJNIBridge(method, fnPtr);

Location:dalvik/vm/Jni.cpp

一个JNI方法并不是直接被调用的,而是通过由Dalvik虚拟机间接地调用,这个用来间接调用JNI方法的函数就称为一个Bridge。这些Bridage函数在真正调用JNI方法之前,会执行一些通用的初始化工作。例如,会将当前线程的状态设置为NATIVE,因为它即将要执行一个Native函数。又如,会为即将要被调用的JNI方法准备好前面两个参数,第一个参数是一个JNIEnv对象,用来描述当前线程的Java环境,通过它可以访问反过来访问Java代码和Java对象,第二个参数是一个jobject对象,用来描述当前正在执行JNI方法的Java对象。

这些Bridage函数实际上仍然不是直接调用地调用JNI方法的,这是因为Dalvik虚拟机是可以运行在各种不同的平台之上,而每一种平台可能都定义有自己的一套函数调用规范,也就是所谓的ABI(Application Binary Interface),这是一个API(Application Programming Interface)不同的概念。ABI是在二进制级别上定义的一套函数调用规范,例如参数是通过寄存器来传递还是堆栈来传递,而API定义是一个应用程序编程接口规范。换句话说,API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译 ,而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。

为了使得运行在不同平台上的Dalvik虚拟机能够以统一的方法来调用JNI方法,这些Bridage函数使用了一个libffi库,它的源代码位于external/libffi目录中。Libffi是一个开源项目,用于高级语言之间的相互调用的处理,它的实现机制可以进一步参考http://www.sourceware.org/libffi/

回到函数dvmUseJNIBridge中,它主要就是根据Dalvik虚拟机的启动选项来为即将要注册的JNI选择一个合适的Bridge函数。如果我们在Dalvik虚拟机启动的时候,通过-Xjnitrace选项来指定了要跟踪参数method所描述的JNI方法,那么函数dvmUseJNIBridge为该JNI方法选择的Bridge函数就为dvmTraceCallJNIMethod,否则的话,就再通过另外一个函数dvmSelectJNIBridge来进一步选择一个合适的Bridge函数。选择好Bridge函数之后,函数dvmUseJNIBridge最终就调用函数dvmSetNativeFunc来执行真正的JNI方法注册操作。

我们假设参数method所描述的JNI方法没有设置为跟踪,因此,接下来,我们就首先分析函数dvmSelectJNIBridge的实现,接着再分析函数dvmSetNativeFunc的实现。

  1. /*
  2. * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
  3. * to point at the actual function.
  4. */
  5. void dvmUseJNIBridge(Method* method, void* func) {
  6. method->shouldTrace = shouldTrace(method);
  7. // Does the method take any reference arguments?
  8. method->noRef = true;
  9. const char* cp = method->shorty;
  10. while (*++cp != '\0') { // Pre-increment to skip return type.
  11. if (*cp == 'L') {
  12. method->noRef = false;
  13. break;
  14. }
  15. }
  16. DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;
  17. dvmSetNativeFunc(method, bridge, (const u2*) func);
  18. }

dvmSelectJNIBridge

介绍如何选择Bridge函数
Dalvik虚拟机提供的Bridge函数主要是分为两类。第一类Bridge函数在调用完成JNI方法之后,会检查该JNI方法的返回结果是否与声明的一致,这是因为一个声明返回String的JNI方法在执行时返回的可能会是一个Byte Array。如果不一致,取决于Dalvik虚拟机的启动选项,它可能会停机。第二类Bridge函数不对JNI方法的返回结果进行上述检查。选择哪一类Bridge函数可以通过-Xcheck:jni选项来决定。不过由于检查一个JNI方法的返回结果是否与声明的一致是很耗时的,因此,我们一般都不会使用第一类Bridge函数。

    此外,每一类Bridge函数又分为四个子类:Genernal、Sync、VirtualNoRef和StaticNoRef,它们的选择规则为:

    1. 一个JNI方法的参数列表中如果包含有引用类型的参数,那么对应的Bridge函数就是Genernal类型的,即为dvmCallJNIMethod_general或者dvmCheckCallJNIMethod_general。

    2. 一个JNI方法如果声明为同步方法,即带有synchronized修饰符,那么对应的Bridge函数就是Sync类型的,即为dvmCallJNIMethod_synchronized或者dvmCheckCallJNIMethod_synchronized。

    3. 一个JNI方法的参数列表中如果不包含有引用类型的参数,并且它是一个虚成员函数,那么对应的Bridge函数就是kJNIVirtualNoRef类型的,即为dvmCallJNIMethod_virtualNoRef或者dvmCheckCallJNIMethod_virtualNoRef。

    4. 一个JNI方法的参数列表中如果不包含有引用类型的参数,并且它是一个静态成员函数,那么对应的Bridge函数就是StaticNoRef类型的,即为dvmCallJNIMethod_staticNoRef或者dvmCheckCallJNIMethod_staticNoRef。

    每一类Bridge函数之所以要划分为上述四个子类,是因为每一个子类的Bridge函数在调用真正的JNI方法之前,所要进行的准备工作是不一样的。例如,Genernal类型的Bridge函数需要为引用类型的参数增加一个本地引用,避免它在JNI方法执行的过程中被回收。又如,Sync类型的Bridge函数在调用JNI方法之前,需要执行同步原始,以避免多线程访问的竞争问题。

    这一步执行完成之后,返回到前面的Step 10中,即函数dvmUseJNIBridge中,这时候它就获得了一个Bridge函数,因此,接下来它就可以调用函数dvmSetNativeFunc来执行真正的JNI方法注册操作了。

dvmSetNativeFunc

Location:dalvik/vm/oo/Class.cpp

    参数method表示要注册JNI方法的Java类成员函数,参数func表示JNI方法的Bridge函数,参数insns表示要注册的JNI方法的函数地址。

    当参数insns的值不等于NULL的时候,函数dvmSetNativeFunc就分别将参数insns和func的值分别保存在参数method所指向的一个Method对象的成员变量insns和nativeFunc中,而当insns的值等于NULL的时候,函数dvmSetNativeFunc就只将参数func的值保存在参数method所指向的一个Method对象成员变量nativeFunc中。
  1. /*
  2. * Replace method->nativeFunc and method->insns with new values. This is
  3. * commonly performed after successful resolution of a native method.
  4. *
  5. * There are three basic states:
  6. * (1) (initial) nativeFunc = dvmResolveNativeMethod, insns = NULL
  7. * (2) (internal native) nativeFunc = <impl>, insns = NULL
  8. * (3) (JNI) nativeFunc = JNI call bridge, insns = <impl>
  9. *
  10. * nativeFunc must never be NULL for a native method.
  11. *
  12. * The most common transitions are (1)->(2) and (1)->(3). The former is
  13. * atomic, since only one field is updated; the latter is not, but since
  14. * dvmResolveNativeMethod ignores the "insns" field we just need to make
  15. * sure the update happens in the correct order.
  16. *
  17. * A transition from (2)->(1) would work fine, but (3)->(1) will not,
  18. * because both fields change. If we did this while a thread was executing
  19. * in the call bridge, we could null out the "insns" field right before
  20. * the bridge tried to call through it. So, once "insns" is set, we do
  21. * not allow it to be cleared. A NULL value for the "insns" argument is
  22. * treated as "do not change existing value".
  23. */
  24. void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func,
  25. const u2* insns)
  26. {
  27. ClassObject* clazz = method->clazz;
  28. assert(func != NULL);
  29. /* just open up both; easier that way */
  30. dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
  31. dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
  32. if (insns != NULL) {
  33. /* update both, ensuring that "insns" is observed first */
  34. method->insns = insns;
  35. android_atomic_release_store((int32_t) func,
  36. (volatile int32_t*)(void*) &method->nativeFunc);
  37. } else {
  38. /* only update nativeFunc */
  39. method->nativeFunc = func;
  40. }
  41. dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
  42. dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
  43. }
调用过程说明
    假设在前面的Step 11中选择的Bridge函数为dvmCallJNIMethod_general,并且结合前面Dalvik虚拟机的运行过程分析一文,我们就可以得到Dalvik虚拟机在运行过程中调用JNI方法的过程:

    1. 调用函数dvmCallJNIMethod_general,执行一些必要的准备工作;

    2. 函数dvmCallJNIMethod_general再调用函数dvmPlatformInvoke来以统一的方式来调用对应的JNI方法;

    3. 函数dvmPlatformInvoke通过libffi库来调用对应的JNI方法,以屏蔽Dalvik虚拟机运行在不同目标平台的细节。

    至此,我们就分析完成Dalvik虚拟机JNI方法的注册过程了。这样,我们就打通了Java代码和Native代码之间的道路。实际上,很多Java和Android核心类的功能都是通过本地操作系统提供的系统调用来完成的,例如,Zygote类的成员函数forkAndSpecialize最终是通过Linux系统调用fork来创建一个Android应用程序进程的,又如,Thread类的成员函数start最终是通过pthread线程库函数pthread_create来创建一个Android应用程序线程的。

1.2 Dynamically Resolve Native Functions for JNI

dvmResolveNativeMethod is the inital nativeFuncJNI for a native method in Java. If a native method in Java isn't binded to a dvmCallJNIMethod_xxx and a native function, then dvmResolveNativeMethod will be invoked as it is the initial function. We will introduce dvmResolveNativeMethod as follows.

1.2.1 dvmResolveNativeMethod

Location:dalvik/vm/native.cpp

注:从执行步骤看出,无论哪种情况,只要找到相应的Native Function,则被绑定后,该函数dvmResolveNativeMehtod将不会再次被调用.

  1. /* * Resolve a native method and invoke it.
  2. *
  3. * This is executed as if it were a native bridge or function. If the
  4. * resolution succeeds, method->insns is replaced, and we don't go through
  5. * here again unless the method is unregistered.
  6. *
  7. * Initializes method's class if necessary.
  8. *
  9. * An exception is thrown on resolution failure.
  10. *
  11. * (This should not be taking "const Method*", because it modifies the
  12. * structure, but the declaration needs to match the DalvikBridgeFunc
  13. * type definition.)
  14. */
  15. void dvmResolveNativeMethod(const u4* args, JValue* pResult,
  16. const Method* method, Thread* self)
  17. {
  18. ClassObject* clazz = method->clazz;
  19. /** If this is a static method, it could be called before the class
  20. * has been initialized.*/
  21. if (dvmIsStaticMethod(method)) {
  22. if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
  23. assert(dvmCheckException(dvmThreadSelf()));
  24. return;
  25. }
  26. } else {
  27. assert(dvmIsClassInitialized(clazz) ||
  28. dvmIsClassInitializing(clazz));
  29. }
  30. /* start with our internal-native methods */
  31. DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
  32. if (infunc != NULL) {(*method->nativeFunc)(args, pResult, method, self);
  33. /* resolution always gets the same answer, so no race here */
  34. IF_LOGVV() {
  35. char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
  36. LOGVV("+++ resolved native %s.%s %s, invoking",
  37. clazz->descriptor, method->name, desc);
  38. free(desc);
  39. }
  40. if (dvmIsSynchronizedMethod(method)) {
  41. ALOGE("ERROR: internal-native can't be declared 'synchronized'");
  42. ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
  43. dvmAbort(); // harsh, but this is VM-internal problem
  44. }
  45. DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
  46. dvmSetNativeFunc((Method*) method, dfunc, NULL);
  47. dfunc(args, pResult, method, self);
  48. return;
  49. }
  50. /* now scan any DLLs we have loaded for JNI signatures */
  51. void* func = lookupSharedLibMethod(method);
  52. if (func != NULL) {
  53. /* found it, point it at the JNI bridge and then call it */
  54. dvmUseJNIBridge((Method*) method, func);
  55. (*method->nativeFunc)(args, pResult, method, self);
  56. return;
  57. }
  58. IF_ALOGW() {
  59. char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
  60. ALOGW("No implementation found for native %s.%s:%s",
  61. clazz->descriptor, method->name, desc);
  62. free(desc);
  63. }
  64. dvmThrowUnsatisfiedLinkError("Native method not found", method);
  65. }

dvmLookupInternalNativeMethod

Location:/dalvik/vm/native/InternalNative.cpp


Invoke Native Method

As we know, Davik Virtual Machine is stared up in Zygote process and receives an instance of JavaVM and an instance of JNIEnv. The JavaVM instance is used to describe the instance of Zygote's Dalvik VM, and JNIEnv is used to describe the JNI environment of Zygote's main thread. Once JNIEnv and JavaVM are ready, JNIEnv's member method--CallStaticVoidMethod is invoked by Zygote process to call the static member method main of com.android.internal.os.ZygoteInit class, say, main is the entrance of Java World.
Now, let's start the discussion with CallStaticVoidMethod.

JNIEnv.CallStaticVoidMethod

The following picture[3] shows How Dalvik VM works.

How Dalvik VM works

Typically, for the invoke of Native Methods, the first 4 steps are enough. Now we'll goto details.

JNIEnv.CallStaticVoidMethod is defined in dalvik/libnativehelper/inlcude/nativehelper/jni.h.

Actually, JNIEnv is a strcture containing the memeber variable functions, a table of callback functions, in a type of JNINativeInterface. Thus, JNIEnv.CallStaticVoidMethod call functions->CallStaticVoidMethodV

  1. void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
  2. {
  3. functions->CallStaticVoidMethodV(this, clazz, methodID, args);
  4. }

This function table named functions comes from gNativeInterface, a global function table in dalvik/vm/jni.cpp. In this table, CallStaticVoidMethodV points to a function with the same name, which is also defined in dalvik/vm/jni.cpp

  1. /*
  2. * Call a static method.
  3. */
  4. #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
  5. ...
  6. static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
  7. jmethodID methodID, va_list args) \
  8. { \
  9. UNUSED_PARAMETER(jclazz); \
  10. ScopedJniThreadState ts(env); \
  11. JValue result; \
  12. dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
  13. if (_isref && !dvmCheckException(ts.self())) \
  14. result.l = (Object*)addLocalReference(ts.self(), result.l); \
  15. return _retok; \
  16. ...

When the CALL_STATIC is replaced, we can get the real implementation of CallStaticMethodV. we can see, dvmCallMethodV is the key point.

dvmCallMethodV

If the called method is a native method, it will call method->nativeFunc directly. Otherwise, it will call dvmInterpret to interpret and execute Java method.

  1. /*
  2. * Issue a method call with a variable number of arguments. We process
  3. * the contents of "args" by scanning the method signature.
  4. *
  5. * Pass in NULL for "obj" on calls to static methods.
  6. *
  7. * We don't need to take the class as an argument because, in Dalvik,
  8. * we don't need to worry about static synchronized methods.
  9. */
  10. void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
  11. bool fromJni, JValue* pResult, va_list args)
  12. {
  13. const char* desc = &(method->shorty[1]); // [0] is the return type.
  14. int verifyCount = 0;
  15. ClassObject* clazz;
  16. u4* ins;
  17. clazz = callPrep(self, method, obj, false);
  18. if (clazz == NULL)
  19. return;
  20. /* "ins" for new frame start at frame pointer plus locals */
  21. ins = ((u4*)self->interpSave.curFrame) +
  22. (method->registersSize - method->insSize);
  23. //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);
  24. /* put "this" pointer into in0 if appropriate */
  25. if (!dvmIsStaticMethod(method)) {
  26. #ifdef WITH_EXTRA_OBJECT_VALIDATION
  27. assert(obj != NULL && dvmIsHeapAddress(obj));
  28. #endif
  29. *ins++ = (u4) obj;
  30. verifyCount++;
  31. }
  32. while (*desc != '\0') {
  33. switch (*(desc++)) {
  34. case 'D': case 'J': {
  35. u8 val = va_arg(args, u8);
  36. memcpy(ins, &val, 8); // EABI prevents direct store
  37. ins += 2;
  38. verifyCount += 2;
  39. break;
  40. }
  41. case 'F': {
  42. /* floats were normalized to doubles; convert back */
  43. float f = (float) va_arg(args, double);
  44. *ins++ = dvmFloatToU4(f);
  45. verifyCount++;
  46. break;
  47. }
  48. case 'L': { /* 'shorty' descr uses L for all refs, incl array */
  49. void* arg = va_arg(args, void*);
  50. assert(obj == NULL || dvmIsHeapAddress(obj));
  51. jobject argObj = reinterpret_cast<jobject>(arg);
  52. if (fromJni)
  53. *ins++ = (u4) dvmDecodeIndirectRef(self, argObj);
  54. else
  55. *ins++ = (u4) argObj;
  56. verifyCount++;
  57. break;
  58. }
  59. default: {
  60. /* Z B C S I -- all passed as 32-bit integers */
  61. *ins++ = va_arg(args, u4);
  62. verifyCount++;
  63. break;
  64. }
  65. }
  66. }
  67. #ifndef NDEBUG
  68. if (verifyCount != method->insSize) {
  69. ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
  70. method->insSize, clazz->descriptor, method->name);
  71. assert(false);
  72. goto bail;
  73. }
  74. #endif
  75. //dvmDumpThreadStack(dvmThreadSelf());
  76. if (dvmIsNativeMethod(method)) {
  77. TRACE_METHOD_ENTER(self, method);
  78. /*Because we leave no space for local variables, "curFrame" points directly at the method arguments.*/
  79. (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
  80. method, self);
  81. TRACE_METHOD_EXIT(self, method);
  82. } else {
  83. dvmInterpret(self, method, pResult);
  84. }
  85. #ifndef NDEBUG
  86. bail:
  87. #endif
  88. dvmPopFrame(self);
  89. }

Native Method Invocation

dvm的中的所有method都是通过一个Method的object进行管理。
All methods in the DVM are managed via Method object. Each method in Java is described via a Method object.

  1. /*
  2. * A method. We create one of these for every method in every class we load, so try to keep the size to a minimum. Much of this comes from and could be accessed in the data held in shared memory.
  3. We hold it all together here for speed.
  4. Everything but the pointers could be held in a shared table generated by the optimizer; if we're willing to convert them to offsets and take the performance hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we could move everything but "nativeFunc". */
  5. struct Method {
  6. /* the class we are a part of */
  7. ClassObject* clazz;
  8. /* access flags; low 16 bits are defined by spec (could be u2?) */
  9. u4 accessFlags;
  10. /** For concrete virtual methods, this is the offset of the method in "vtable". For abstract methods in an interface class, this is the offset of the method in "iftable[n]->methodIndexArray". */
  11. u2 methodIndex;
  12. /*Method bounds; not needed for an abstract method. For a native method, we compute the size of the argument list, and set "insSize" and "registerSize" equal to it. */
  13. u2 registersSize; /* ins + locals */
  14. u2 outsSize;
  15. u2 insSize;
  16. /* method name, e.g. "<init>" or "eatLunch" */
  17. const char* name;
  18. /*Method prototype descriptor string (return and argument types).
  19. *
  20. * TODO: This currently must specify the DexFile as well as the proto_ids index, because generated Proxy classes don't have a DexFile. We can remove the DexFile* and reduce the size of this struct if we generate a DEX for proxies.*/
  21. DexProto prototype;
  22. /* short-form method descriptor string */
  23. const char* shorty;
  24. /*The remaining items are not used for abstract or native methods. (JNI is currently hijacking "insns" as a function pointer, set after the first call. For internal-native this stays null.) */
  25. /* the actual code */
  26. const u2* insns; /* instructions, in memory-mapped .dex */
  27. /* JNI: cached argument and return-type hints */
  28. int jniArgInfo;
  29. /*JNI: native method ptr; could be actual function or a JNI bridge. We don't currently discriminate between DalvikBridgeFunc and DalvikNativeFunc; the former takes an argument superset (i.e. two extra args) which will be ignored. If necessary we can use insns==NULL to detect JNI bridge vs. internal native. */
  30. DalvikBridgeFunc nativeFunc;
  31. /*JNI: true if this static non-synchronized native method (that has no reference arguments) needs a JNIEnv* and jclass/jobject. Libcore uses this.*/
  32. bool fastJni;
  33. /*JNI: true if this method has no reference arguments. This lets the JNI bridge avoid scanning the shorty for direct pointers that need to be converted to local references. TODO: replace this with a list of indexes of the reference arguments.*/
  34. bool noRef;
  35. /*JNI: true if we should log entry and exit. This is the only waydevelopers can log the local references that are passed into their code. Used for debugging JNI problems in third-party code.*/
  36. bool shouldTrace;
  37. /*Register map data, if available. This will point into the DEX file if the data was computed during pre-verification, or into the linear alloc area if not.*/
  38. const RegisterMap* registerMap;
  39. /* set if method was called during method profiling */
  40. bool inProfile;
  41. };

Summary

When a native library is loaded, the JNI_OnLoad function in library will be automatically loaded. Usually, some initial stuffs and native method registeration via jniRegisterNativeMethod are processed in JNI_OnLoad.

If a native method is registered in JNI_OnLoad, then the method->nativeFunc will be changed from dvmResolveNativeMethod to dvmCallJNIMehtod_general, at the same time, method->insns will be set to the native function, which is the implementation of the JNI method.

If the native method isn't registered in JNI_OnLoad, then when the native method is called in Java, it will invoke the method->nativeFunc's default vaule, dvmResolveNativeMethod.

In dvmResolveNativeMethod, it will try to find a function, which matches the JNI method based on the JNI specification, and bind it to the current JNI method, then invoke it. If it couldn't find the right native function, it will throw an exception.

When a Java method is called, dvm will examine whether the method is a native method. If it's a native method, it will invoke method->nativeFunc. Otherwise, it will use related intepreter to executate its java code.


References

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注