jni方法的注册和调用

来源:互联网 发布:淘宝怎么刷単赚钱 编辑:程序博客网 时间:2024/05/02 13:05

转自:http://weihe6666.iteye.com/blog/2225011

JNI在android中起重要作用,是连接java层和dalvik重要的通道,java层的开发也比较简单,有两种注册方式:

1. 按照JNI格式声明native函数并在c文件中定义此native函数
2. 动态注册,在c文件中重写JNI_OnLoad函数,并在JNI_OnLoad中调用JNIEnv函数RegisterNatives注册native方法。


看似简单的注册和调用,背后隐藏着复杂的理论,下面就来分析一下RegisterNatives方法的逻辑。


RegisterNatives源码如下:jni.cpp
Java代码  收藏代码
  1. static jint RegisterNatives(JNIEnv* env, jclass jclazz,  
  2.     const JNINativeMethod* methods, jint nMethods)  
  3. {  
  4.   .......  
  5.     for (int i = 0; i < nMethods; i++) {  
  6.         if (!dvmRegisterJNIMethod(clazz, methods[i].name,  
  7.                 methods[i].signature, methods[i].fnPtr))  
  8.         {  
  9.             return JNI_ERR;  
  10.         }  
  11.     }  
  12.     return JNI_OK;  
  13. }  


这里主要逻辑调用dvmRegisterJNIMethod方法。

继续跟踪,此方法比较复杂,我们只分析关键语句。
Java代码  收藏代码
  1. method->fastJni = fastJni;  
  2. dvmUseJNIBridge(method, fnPtr);  
  3.   
  4. ALOGV("JNI-registered %s.%s:%s", clazz->descriptor, methodName, signature);  
  5. return true;  


这里调用了dvmUseJNIBridge函数,这是注册的核心函数,其关键语句需要详细分析:
Java代码  收藏代码
  1. void dvmUseJNIBridge(Method* method, void* func) {  
  2.     method->shouldTrace = shouldTrace(method);  
  3.   
  4.     // Does the method take any reference arguments?  
  5.     method->noRef = true;  
  6.     const char* cp = method->shorty;  
  7.     while (*++cp != '\0') { // Pre-increment to skip return type.  
  8.         if (*cp == 'L') {  
  9.             method->noRef = false;  
  10.             break;  
  11.         }  
  12.     }  
  13.   
  14.     DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;  
  15.     dvmSetNativeFunc(method, bridge, (const u2*) func);  
  16. }  


这里的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函数,这个函数主要是通过
Java代码  收藏代码
  1. dvmPlatformInvoke(env,  
  2.         (ClassObject*) staticMethodClass,  
  3.         method->jniArgInfo, method->insSize, modArgs, method->shorty,  
  4.         (void*) method->insns, pResult);  


来对native方法调用,而dvmPlatformInvoke通过libffi进行method调用,以屏蔽Dalvik虚拟机运行在不同目标平台的细节。



回头在看一下dvmSetNativeFunc函数:
Java代码  收藏代码
  1. void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func,  
  2.     const u2* insns)  
  3. {  
  4.    ......  
  5.     if (insns != NULL) {  
  6.         /* update both, ensuring that "insns" is observed first */  
  7.         method->insns = insns;  
  8.         android_atomic_release_store((int32_t) func,  
  9.             (volatile int32_t*)(void*) &method->nativeFunc);  
  10.     } else {  
  11.         /* only update nativeFunc */  
  12.         method->nativeFunc = func;  
  13.     }  
  14.     ......  


这里主要是对注册的nativeFunc和DalvikBridgeFunc func进行一个关联,调用DalvikBridgeFunc func时,通过dvmPlatformInvoke调用nativeFunc函数。

整体jni函数注册已经分析完了,主要是处理nativefunc和bridge之间的跳转。