转自: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
- 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之间的跳转。