JNI在android中起重要作用,是连接java层和dalvik重要的通道,java层的开发也比较简单,有两种注册方式:
1. 按照JNI格式声明native函数并在c文件中定义此native函数
2. 动态注册,在c文件中重写JNI_OnLoad函数,并在JNI_OnLoad中调用JNIEnv函数RegisterNatives注册native方法。
看似简单的注册和调用,背后隐藏着复杂的理论,下面就来分析一下RegisterNatives方法的逻辑。
RegisterNatives源码如下:jni.cpp
static jint RegisterNatives(JNIEnv* env, jclass jclazz, const JNINativeMethod* methods, jint nMethods) { ....... for (int i = 0; i < nMethods; i++) { if (!dvmRegisterJNIMethod(clazz, methods[i].name, methods[i].signature, methods[i].fnPtr)) { return JNI_ERR; } } return JNI_OK; }
这里主要逻辑调用dvmRegisterJNIMethod方法。
继续跟踪,此方法比较复杂,我们只分析关键语句。
method->fastJni = fastJni; dvmUseJNIBridge(method, fnPtr); ALOGV("JNI-registered %s.%s:%s", clazz->descriptor, methodName, signature); return true;
这里调用了dvmUseJNIBridge函数,这是注册的核心函数,其关键语句需要详细分析:
void dvmUseJNIBridge(Method* method, void* func) { method->shouldTrace = shouldTrace(method); // Does the method take any reference arguments? method->noRef = true; const char* cp = method->shorty; while (*++cp != '\0') { // Pre-increment to skip return type. if (*cp == 'L') { method->noRef = false; break; } } DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod; dvmSetNativeFunc(method, bridge, (const u2*) func); }
这里的DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;是一个bridge函数,也称之为函数中转站,当调用native方法时,会通过bridge调用,由于dalvik对不同平台的支持,调用native方法为了适应多平台而采用的一种机制。
下面一段来自http://blog.csdn.net/luoshengyang/article/details/8923483
这些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/。
我们看一下dvmCallJNIMethod函数,这个函数主要是通过
dvmPlatformInvoke(env, (ClassObject*) staticMethodClass, method->jniArgInfo, method->insSize, modArgs, method->shorty, (void*) method->insns, pResult);
来对native方法调用,而dvmPlatformInvoke通过libffi进行method调用,以屏蔽Dalvik虚拟机运行在不同目标平台的细节。
回头在看一下dvmSetNativeFunc函数:
void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func, const u2* insns) { ...... if (insns != NULL) { /* update both, ensuring that "insns" is observed first */ method->insns = insns; android_atomic_release_store((int32_t) func, (volatile int32_t*)(void*) &method->nativeFunc); } else { /* only update nativeFunc */ method->nativeFunc = func; } ......
这里主要是对注册的nativeFunc和DalvikBridgeFunc func进行一个关联,调用DalvikBridgeFunc func时,通过dvmPlatformInvoke调用nativeFunc函数。
整体jni函数注册已经分析完了,主要是处理nativefunc和bridge之间的跳转。