欢迎关注大数据技术架构与案例微信公众号:过往记忆大数据
过往记忆博客公众号iteblog_hadoop
欢迎关注微信公众号:
过往记忆大数据

《深入理解 JNI》:JNI 函数调用

本文是《深入理解 JNI》专题的第 3 篇,共 5 篇:

调用本地方法

在JNI中,从Java代码调用本地方法是一个核心功能。这个过程涉及到Java端的声明、本地方法的实现,以及两者之间的连接。以下是如何在JNI中调用本地方法的详细步骤。

在Java中声明本地方法

首先,在Java类中声明本地方法。使用`native`关键字标记这些方法,但不需要提供方法体。这些方法的具体实现在本地代码中完成。

public class NativeMethodExample {
    // 声明本地方法
    public native void nativeMethod();

    // 加载包含本地方法实现的共享库
    static {
        System.loadLibrary("native-lib");
    }

    // 主方法,用于测试本地方法
    public static void main(String[] args) {
        new NativeMethodExample().nativeMethod();
    }
}

在上面的代码中,`nativeMethod`是一个本地方法,它将在本地代码中实现。`System.loadLibrary`方法用于加载包含本地方法实现的共享库。

生成头文件

使用`javac`命令编译Java类,然后使用`javah`工具生成C语言的头文件。这个头文件包含了本地方法的C语言声明。

javac NativeMethodExample.java
javah NativeMethodExample

这将生成一个名为`NativeMethodExample.h`的头文件,其中包含了`nativeMethod`方法的C语言声明。

实现本地方法

接下来,编写C代码来实现头文件中声明的函数。创建一个C文件,例如`native-lib.c`,并实现`nativeMethod`函数。

#include <jni.h>
#include <stdio.h>
#include "NativeMethodExample.h"

JNIEXPORT void JNICALL Java_NativeMethodExample_nativeMethod(JNIEnv *env, jobject thisObj) {
    printf("Native method called!
");
}

在这个C函数中,我们使用了`JNIEXPORT`和`JNICALL`宏来确保函数可以被Java虚拟机正确调用。`JNIEnv *env`参数提供了访问JNI函数表的指针,而`jobject thisObj`参数是对调用该本地方法的Java对象的引用。

编译共享库

现在我们需要编译C代码并生成共享库。这个步骤会根据操作系统的不同而有所变化。

在Linux上

  gcc -shared -fPIC -o libnative-lib.so native-lib.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux
  

在macOS上

  gcc -dynamiclib -o libnative-lib.dylib native-lib.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin
  

在Windows上
使用MinGW或Visual Studio的命令行工具来编译生成DLL文件。

运行Java程序

最后,我们可以运行Java程序来测试本地方法。确保共享库位于Java的库路径中,然后运行Java类。

java NativeMethodExample

如果一切设置正确,你应该会在控制台看到输出:

Native method called!

通过这个过程,我们可以看到从Java调用本地方法的基本步骤。这个简单的示例展示了JNI的核心功能,即允许Java代码调用本地代码。在实际的项目中,本地方法可能会更加复杂,涉及到更多的数据类型转换、对象操作和异常处理等。但这个基础示例为理解JNI的工作原理提供了一个良好的起点。

调用Java方法

在前面的内容中,我们了解了如何从Java调用本地方法。现在,我们将探讨如何从本地代码(C/C++)中调用Java方法。这是JNI功能的另一个重要方面,它允许本地代码与Java对象进行交互,调用它们的方法。

获取JNIEnv指针

在本地方法中,可以通过函数参数获得一个`JNIEnv`指针。这个指针是访问JNI函数表的入口,通过它我们可以调用JNI提供的各种函数,包括调用Java方法的函数。

获取MethodID

要调用Java方法,首先需要获取该方法的`MethodID`。这可以通过调用`JNIEnv`指针的`GetMethodID`函数来实现。`GetMethodID`需要三个参数:Java对象的类、方法的名称和方法的签名。

:可以通过`GetObjectClass`函数获取调用本地方法的Java对象的类。
方法名称:是Java方法的名字,例如"toString"。
方法签名:是一个描述方法参数和返回类型的字符串。例如,对于没有参数且返回类型为String的方法,其签名为`()Ljava/lang/String;`。

调用Java方法

一旦我们有了`MethodID`,就可以使用`JNIEnv`指针的相应函数来调用Java方法。根据方法的返回类型,有不同的函数可供选择,例如:

  • `CallVoidMethod`:调用无返回值的Java方法。
  • `CallObjectMethod`:调用返回对象引用的Java方法。
  • `CallIntMethod`:调用返回int类型的Java方法。
  • ...等等,对于每种返回类型都有对应的调用函数。

示例代码:从C/C++调用Java方法

假设我们有一个Java类`JavaMethodExample`,其中有一个方法`sayHello`,我们想要从C/C++代码中调用它。

public class JavaMethodExample {
    public void sayHello() {
        System.out.println("Hello from Java!");
    }

    public native void callSayHelloFromNative();

    static {
        System.loadLibrary("method-example");
    }

    public static void main(String[] args) {
        new JavaMethodExample().callSayHelloFromNative();
    }
}
#include <jni.h>
#include <stdio.h>
#include "JavaMethodExample.h"

JNIEXPORT void JNICALL Java_JavaMethodExample_callSayHelloFromNative(JNIEnv *env, jobject thisObj) {
    // 获取Java对象的类
    jclass cls = (*env)->GetObjectClass(env, thisObj);
    // 获取sayHello方法的MethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "sayHello", "()V");
    if (mid == 0) {
        return; // 方法未找到
    }
    // 调用sayHello方法
    (*env)->CallVoidMethod(env, thisObj, mid);
}

在这个示例中,我们首先获取了Java对象的类,然后获取了`sayHello`方法的`MethodID`,最后使用`CallVoidMethod`函数调用了这个方法。当运行这个程序时,它将在控制台打印出"Hello from Java!"。

调用Java方法时需要注意以下几点:

  • 确保方法名称和签名正确无误。
  • 处理可能出现的异常,例如方法未找到或非法访问。
  • 在调用完成后,释放局部引用,以避免局部引用表溢出。

通过这种方式,JNI使得本地代码能够调用Java对象的方法,从而实现了双向的交互能力。这是JNI强大功能的体现之一,它允许Java和本地代码紧密协作,共同完成复杂的任务。

本博客文章除特别声明,全部都是原创!
原创文章版权归过往记忆大数据(过往记忆)所有,未经许可不得转载。
本文链接: 【《深入理解 JNI》:JNI 函数调用】(https://www.iteblog.com/archives/10222.html)
喜欢 (0)
分享 (0)
发表我的评论
取消评论

表情
本博客评论系统带有自动识别垃圾评论功能,请写一些有意义的评论,谢谢!