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之间的跳转。
分享到:
相关推荐
在这个过程中,关键点包括正确地处理JNI数据类型,如 `jobject`、`JNIEnv *` 和 `jmethodID`,以及在C++中调用Java方法。JNI提供了一套API来实现这些功能,例如 `CallVoidMethod()`、`FindClass()` 和 `GetMethodID...
从`System.loadLibrary`方法的调用开始,经过一系列步骤最终将本地库中的JNI方法注册到Dalvik虚拟机中,使得Java层可以调用这些方法。通过对这一过程的理解,开发者能够更好地掌握JNI技术的应用,提高Android应用的...
在示例中,`ModuleAAA`类定义了两个本地方法`modulePowerOn()`和`modulePowerOff()`,它们分别调用了`JNI_modulePowerOn()`和`JNI_modulePowerOff()`。在C++源码`android_hardware_moduleAAA.cpp`中,我们看到了这...
2. 使用`RegisterNatives`函数:在`JNI_OnLoad`中,通过`JNIEnv`指针调用`RegisterNatives`,传入Java类的类对象和本地方法的数组来注册函数。 3. 编写本地方法:定义你的C/C++函数,这些函数将作为Java类的本地...
在Android开发中,有时我们需要利用Java Native Interface (JNI) 来调用C/C++的库,以实现一些高性能或者特定平台的功能,例如本案例中的串口Socket通讯和USB驱动。JNI是Java平台标准的一部分,它允许Java代码和其他...
在这个文件中,可以定义JNI函数,使用`JNIEnv*`指针和`jobject`来访问Java对象和方法。 4. **注册JNI函数** 在Java代码中,使用`System.loadLibrary("native-lib")`加载本地库,其中`"native-lib"`是C/C++库的名字...
动态注册的原理:JNI 允许我们提供一个函数映射表,注册给 JVM,这样 JVM 就可以用函数映射表来调用相应的函数, 而不必通过函数名来查找相关函数(这个查找效率很低,函数名超级长)流程更加清晰可控,效率更高....
2. **注册JNI函数**:在C/C++的JNI代码中,需要使用`RegisterNatives`函数或者在JNI初始化方法中注册要调用的Java方法。 3. **获取Java对象引用**:使用`env->FindClass`查找Java类,然后用`env->GetMethodID`获取...
在IT行业中,转换...总结来说,通过Jcom调用JNI将Office文档转换为PDF是一种结合了Java和Windows COM组件技术的解决方案,它允许Java开发者利用已有的Office转换能力,但这种方法在性能和资源管理上可能存在挑战。
4. **加载和注册JNI函数**:在Java代码中,使用`System.loadLibrary`方法加载Delphi编译生成的DLL,并通过`java.lang.ClassLoader`注册JNI函数。这样,Java就可以调用Delphi实现的函数了。 5. **调用Java类和包**:...
JNI支持的Java调用C/C++的流程大致如下: 1. **编写Java代码并声明native方法**:首先需要在Java类中声明一个或多个native方法,这些方法将在后续步骤中通过C/C++实现。 ```java public class HelloWorld { ...
静态注册是早期JNI常用的方法,它需要在Java类中声明native方法,并在对应的C/C++源文件中使用`JNIEXPORT`和`JNICALL`宏来定义这些方法。例如: ```java public class MyJavaClass { static { System.loadLibrary...
#### 二、JNI调用流程详解 ##### Java调用Native方法 - **静态绑定**: 1. 使用`javac`命令编译Java源文件生成`.class`文件。 2. 使用`javah`工具根据`.class`文件生成C/C++头文件。 3. 在本地代码中实现这些...
- 类和方法的查找:如何查找Java类及其方法ID和字段ID。 - 调用Java方法:区分静态方法和实例方法,以及同步和异步方法的调用。 - 注册本地方法:`RegisterNatives`函数用于批量注册本地方法。 - 回调机制:Java可以...
2. **注册本地方法**:在`JNI_OnLoad`函数中,C++代码会注册需要的本地方法,这些方法将被Java代码调用。 3. **创建线程**:C++中创建多线程,每个线程在运行时会调用预先注册的本地方法。 4. **回调Java方法**:...
2. **注册本地方法**:Java类加载时,通过JNI注册表将本地方法与Java方法关联。 3. **调用本地方法**:Java代码通过反射或直接调用本地方法。 4. **执行本地方法**:本地方法执行,可以访问Java对象、执行Java操作。...
JNI提供了多种函数来调用Java的方法,包括静态方法和实例方法。例如,`CallVoidMethod`用于调用返回值为void的方法,`GetObjectField`用于获取对象的字段值。 5. **本地方法的注册** 在C/C++代码中,需要通过`...
// 示例代码,实际应替换为你的.jar包的类和方法 QAndroidJniObject yourClass = QAndroidJniObject::fromString("your/package/YourClassInJar"); QAndroidJniObject::callMethod("yourMethod", "()V", your...
这个"Android JNI示例"提供了学习JNI调用流程的实践指导。 JNI的核心概念包括以下几个部分: 1. **本地方法**: 在Java类中声明但由本地(非Java)代码实现的方法。这些方法通常用`native`关键字标识。 2. **...
- **本地方法签名**:每个本地方法都有一个特定的签名,由返回类型、参数类型和方法名组成,用于JNI识别。 - **本地方法注册**:在Java代码中,必须使用`System.loadLibrary`加载动态链接库,然后通过`...