使用C/C++实现Java的Native方法接口(JNI)(2)实例详解(C语言版本)

JNI编程(C/C++)

第1节:快速上手

一个简单的demo,快速跑通流程,详见使用C/C++实现Java的Native方法接口(JNI)(1)快速上手

第2节:实例详解(C语言版本)

本节针对第1节中的内例子详细说明(C)

使用Java的javah工具生成.h

生成出来的.h文件(路径是$ProjectDir/jni/pers_h01c_jni_helloJni.h)如下

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class pers_h01c_jni_helloJni */

#ifndef _Included_pers_h01c_jni_helloJni
#define _Included_pers_h01c_jni_helloJni
#ifdef __cplusplus
extern "C" {
    
    
#endif
/*
 * Class:     pers_h01c_jni_helloJni
 * Method:    helloWorld
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_pers_h01c_jni_helloJni_helloWorld
  (JNIEnv *, jobject, jstring);

/*
 * Class:     pers_h01c_jni_helloJni
 * Method:    staticHelloWorld
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_pers_h01c_jni_helloJni_staticHelloWorld
  (JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif

可以看到该.h导出了两个函数,和之前在java里面定义的native方法一一对应

JNIEXPORT void JNICALL Java_pers_h01c_jni_helloJni_helloWorld
  (JNIEnv *, jobject, jstring);
  • 对应于pers.h01c.jni.helloWorld里的 helloWorld方法
  • JNIEnv * 类型的指针指向Jni的环境对象
  • jobject 类型的参数对应于java里的一个pers.h01c.jni.helloWorld 对象(类的实例)
  • jstring 类型传入的参数的类型(helloWorld(String inputArg))
JNIEXPORT jstring JNICALL Java_pers_h01c_jni_helloJni_staticHelloWorld
  (JNIEnv *, jclass, jstring);
  • 对应于pers.h01c.jni.helloWorld里的 staticHelloWorld方法
  • JNIEnv * 和 jstring同上
  • 这里的第二个参数变成了jclass类型,对应于pers.h01c.jni.helloWorld ,因为这是一个静态方法(static),只能对类进行操作

根据生成的.h编写对应的C实现

编写pers_h01c_jni_helloJni_impl.c(文件路径$ProjectDir/jni/pers_h01c_jni_helloJni_impl.c),其中包括对.h里导出方法的实现

#include <stdio.h>

#include "pers_h01c_jni_helloJni.h" // 需要引入之前的.h文件

JNIEXPORT void JNICALL Java_pers_h01c_jni_helloJni_helloWorld (JNIEnv * env, jobject obj, jstring str){
    
    
    const char * name = (*env) -> GetStringUTFChars(env, str, NULL); // 读取传入的UTF-编码字符串
    printf("hello %s", name);
    (*env) -> ReleaseStringUTFChars(env, str, name); // 释放字符串对应的内存
};

JNIEXPORT jstring JNICALL Java_pers_h01c_jni_helloJni_staticHelloWorld (JNIEnv * env, jclass cls, jstring str){
    
    
    const char * name = (*env) -> GetStringUTFChars(env, str, NULL);

    jstring ret = (*env) -> NewStringUTF(env, name); // 创建新的java string
    return ret;
}

关于env的使用区别说明

JniEnv指针指向JVM虚拟机内的环境,由于C语言没有对象这一概念,因此需要使用如下方法调用env的方法

(*env) -> GetStringUTFChars(env, str, NULL);

在C++中则有对象概念,因此可以直接使用

env->GetStringUTFChars(jstr, nullptr);

编译为C动态链接库

编译所需要include的文件有jni/下的pers_h01c_jni_helloJni.h、$JAVA_HOME/include下的jni.h、以及$JAVA_HOME/include/darwin下的jni_md.h(这里是darwin的原因是实验环境是MacOS操作系统,其他的系统的名字不一样,例如Windows可能是win32)

MacOS(darwin)编译命令

export JNI_LIB_NAME=helloJni
gcc -dynamiclib -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -shared -o lib/lib$JNI_LIB_NAME.dylib jni/pers_h01c_jni_helloJni_impl.c

注意输出的文件名一定要是lib开头,这是JNI所规定的,文件格式为dylib。

Windows的编译命令稍有不同,一方面dynamiclib选项换了,另一方面输出文件不需要lib开头,格式为dll。

gcc -Wl,--add-stdcall-alias -I"$JAVA_HOME$\include" -I"$JAVA_HOME$\include\win32" -shared -o ./lib/$JNI_LIB_NAME$.dll ./jni/pers_h01c_jni_helloJni_impl.c

修改Java代码并测试

在第一步编写的java类中静态引入动态链接库,库名字和上面的JNI_LIB_NAME一致
这里以C语言版本为例,C++版本的流程一样。

// file location: $ProjectDir/src/pers/h01c/jni/helloJni.java

package pers.h01c.jni;

public class helloJni {
    
    

    static {
    
    
        System.loadLibrary("helloJni"); // 注意这个库必须要在java.library.path里
    }

    public native void helloWorld(String inputArg);
    public native static String staticHelloWorld(String inputArg);

}

如果报无法找到库的错,需要在命令行运行的时候加入VM option:

- Djava.library.path=$ProjectDir/lib/

如果是Intellj IDE环境下,则$ProjectDir的位置应该是Intellj的内置宏:$ProjectFileDir$

- Djava.library.path=$ProjectFileDir$/lib/

第3节:实例详解(C++语言版本)

本节针对第1节中的内例子详细说明(C++),详见使用C/C++实现Java的Native方法接口(JNI)(3)实例详解(C++语言版本)

第4节:JNI数据类型

本节介绍了JNI中定义的部分数据类型,详见使用C/C++实现Java的Native方法接口(JNI)(4)JNI数据类型

第5节:jstring类和jobject类的等对象数据的方法

本节详细描述了JNI中最常用的jstring(java.lang.String)和jobject (Obejct)的相关操作方法,详见使用C/C++实现Java的Native方法接口(JNI)(5)jstring类和jobject类的等对象数据的方法

第6节:多种JNI数据类型的代码实例

本节结合前面1-5节的内容,编写了一个包含多种数据类型的实例JNI-C++代码,详见使用C/C++实现Java的Native方法接口(JNI)(6)多种JNI数据类型代码实例

附录:代码

整个项目的资源打包链接:JNI_C/C++_Demo

猜你喜欢

转载自blog.csdn.net/O_1CxH/article/details/125587775