Dalvik虚拟机JNI方法的注册过程分析 (九)

2014-11-24 10:58:08 · 作者: · 浏览: 9
Name, signature);
if (method == NULL)
method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
if (method == NULL) {
LOGW("ERROR: Unable to find decl for native %s.%s:%s\n",
clazz->descriptor, methodName, signature);
goto bail;
}

if (!dvmIsNativeMethod(method)) {
LOGW("Unable to register: not native: %s.%s:%s\n",
clazz->descriptor, methodName, signature);
goto bail;
}

if (method->nativeFunc != dvmResolveNativeMethod) {
/* this is allowed, but unusual */
LOGV("Note: %s.%s:%s was already registered\n",
clazz->descriptor, methodName, signature);
}

dvmUseJNIBridge(method, fnPtr);

LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName,
signature);
result = true;

bail:
return result;
} 这个函数定义在文件dalvik/vm/Jni.c中。

函数dvmRegisterJNIMethod在注册参数methodName所描述的JNI方法之前,首先会进行一系列的检查,包括:

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的操作。

Step 10. dvmUseJNIBridge


[cpp] /**
* Returns true if the -Xjnitrace setting implies we should trace 'method'.
*/
static bool shouldTrace(Method* method)
{
return gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace);
}

/*
* Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
* to point at the actual function.
*/
void dvmUseJNIBridge(Method* method, void* func)
{
DalvikBridgeFunc bridge = shouldTrace(method)
dvmTraceCallJNIMethod
: dvmSelectJNIBridge(method);
dvmSetNativeFunc(method, bridge, func);
}

/**
* Returns true if the -Xjnitrace setting implies we should trace 'method'.
*/
static bool shouldTrace(Method* method)
{
return gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace);
}

/*
* Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
* to point at the actual function.
*/
void dvmUseJNIBridge(Method* method, void* func)
{
DalvikBridgeFunc bridge = shouldTrace(method)
dvmTraceCallJNIMethod
: dvmSelectJNIBridge(method);
dvmSetNativeFunc(method, bridge, func);
}
这个函数定义在文件dalvik/vm/Jni.c中。

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

这些Bridage函数实际上仍然不是直接调用地调用JNI方法的,这是因为Dalvik虚拟机是可以运行在各种不同的平台之上,而每一种平台可能都定义有自己的一套