JNI编程(C/C++)
文章目录
第1节:快速上手
一个简单的demo,快速跑通流程,详见使用C/C++实现Java的Native方法接口(JNI)(1)快速上手
第2节:实例详解(C语言版本)
本节针对第1节中的内例子详细说明(C),详见使用C/C++实现Java的Native方法接口(JNI)(2)实例详解(C语言版本)
第3节:实例详解(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++实现
C++有string库,所以对字符串的支持更好,因此可以先写jstring和c++ string互转的处理函数
std::string jstring2string(JNIEnv*env, jstring jstr)
{
const char* tmpStr = env->GetStringUTFChars(jstr, nullptr);
std::string ret(tmpStr);
env->ReleaseStringUTFChars(jstr, tmpStr);
return ret;
}
jstring string2jstring(JNIEnv* env, std::string str)
{
return env->NewStringUTF(str.c_str());
}
在c++ string类的接口的加持下,可以比C字符数组更方便地操纵字符串,尤其是UTF字符串:
#include <iostream>
#include <string>
#include "pers_h01c_jni_helloJni.h"
using namespace std;
std::string jstring2string(JNIEnv*env, jstring jstr) {// 省略,见上方代码}
jstring string2jstring(JNIEnv* env, std::string str) {// 省略,见上方代码}
JNIEXPORT void JNICALL Java_pers_h01c_jni_helloJni_helloWorld (JNIEnv * env, jobject jobj, jstring str){
const string &s = jstring2string(env, str);
string cstring0 = "jni-cpp:" + s;
cout << cstring0 << endl;
}
JNIEXPORT jstring JNICALL Java_pers_h01c_jni_helloJni_staticHelloWorld(JNIEnv * env, jclass jcls, jstring str){
const string &s = jstring2string(env, str);
string cstring0 = "input_string=" + s;
return string2jstring(env, cstring0);
}
关于env的使用区别说明
JniEnv指针指向JVM虚拟机内的环境,由于C语言没有对象这一概念,因此需要使用如下方法调用env的方法
(*env) -> GetStringUTFChars(env, str, NULL);
在C++中则有对象概念,因此可以直接使用
env->GetStringUTFChars(jstr, nullptr);
编译为C++动态链接库
由于gcc对C++语言只能进行编译但不能进行链接,因此需要用g++来编译,编译的命令中只要把gcc换成g++即可
MacOS下编译
export JNI_LIB_NAME=helloJniCpp
g++ -dynamiclib -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -shared -o lib/lib$JNI_LIB_NAME.dylib jni/pers_h01c_jni_helloJni_impl.cpp
另外C/C++的代码也可以使用cmake等工具编译,可以使用CLion IDE编写C/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/
第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