JNI技术实战

本文运行环境:

  1. 开发工具:

    1)Java:STS(Spring 优化过的 eclipse)

    2)C++:Microsoft Visual Studio 2010 Ultimate(VS 2010 旗舰版)

  2. JDK 环境:JDK 1.8.0_144

  3. 开发平台:Windows 7 x64

  4. 源文件编码字符集:Java 源码文件使用UTF8编码,C++源文件使用UTF8编码

步骤1. 编写 JNI 文件的 Java 源码

使用 STS 创建一个最简单的 Java 工程:JNI_demo,并创建org.woodwhales.king.JniTest类 ,该类有类;两个原生的方法getMessage()plus()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package org.woodwhales.king;

public class JniTest {

static {
try {
System.loadLibrary("TestJni");
} catch (Exception e) {
System.out.println("load dll error, cause : " + e.getMessage());
}
}

public JniTest() {
}

// 获取消息
public native String getMessage(String str);

// 两数相加
public native Integer plus(Integer x, Integer y);
}

注意:编写Java源码之前一定要设置文件是UTF-8编码保存的。

步骤2. 生成 JNI 头文件

编写完成之后,右击项目找到项目根目录对应的资源管理器:

进入项目的bin目录下,运行 dos 命令窗口,执行如下命令生成 JNI 头文件:

1
javah -classpath . -jni org.woodwhales.king.JniTest

命令执行正确,窗口不会提示任何信息,此时当前 bin 目录下会生成一个名为:org_woodwhales_king_JniTest.h的头文件:

步骤3. 创建动态链接库项目

打开 VS 2010 开发工具,初始化界面如下:

点击左上角的文件菜单,创建一个项目:

进入向导界面,直接下一步:

选择DLL动态链接库项目,并勾选空项目:

创建成功后,开发界面视图如下:

此时,右击该项目,进入资源管理器文件目录:

可以看到项目目录下,还有个TestJni目录,这个目录就是开发目录,外面的其他文件,是VS2010开发工具自动生成的开发环境相关文件:

步骤4. JNI相关头文件准备

将步骤2 中生成的JNI头文件拷贝一份到步骤3项目的开发目录中:

另外需要将在编写Java源文件所使用的JDK中自带的jni.hjni_md.h头文件也拷贝一份到这个开发目录:

步骤5. 创建项目目录结构

将步骤4中的头文件添加到 VS 2010 开发工具视图的头文件目录中:

添加头文件到头文件目录之后,再创建一个C++源文件到源文件目录中:

选择创建cpp文件,文件名的命名随意写即可:

步骤6. 开发

开发前的问题注意

前面所有步骤顺利完成之后,开始准备开发,但是有三点需要注意:

问题1:JNI 头文件引用设置

将JNI头文件中引用jni.h的代码修改为双引号引用:

尖括号是引用系统库,引用非系统库使用双引号。

问题2:源文件编码设置

设置MyDLL源文件的编码格式:双击打开MyDLL.cpp文件,让光标在编辑区,然后点击右上角文件菜单,选择高级保存选项,设置文件编码格式为无签名的UTF-8,设置完成之后,保存当前空的源文件即可。

VS 2010 默认创建的文件都是GB2312编码的,这会在处理字符串的时候带来很多转码的麻烦。

问题3:32/64 位问题

点击编辑区上面的Debug下拉框,点击配置管理器,设置项目的解决方案生成的是 64位 项目:

新建解决方案:

选择生成x64的活动解决方案:

最后设置配为:Release

后续生成 dll 文件的时候,工具看到中文也会提示这样的信息:

warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失。

因此将源码文件设置成 UTF-8 编码是很有必要的。

开发JNI

MyDLL.cpp源码中开始引入步骤5中的头文件org_woodwhales_king_JniTest.h,实现头文件的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "org_woodwhales_king_JniTest.h"
#include "jni.h"

JNIEXPORT jstring JNICALL Java_org_woodwhales_king_JniTest_getMessage
(JNIEnv * env, jobject thisObj, jstring j_str) {
const char *c_str = NULL;
char buff[2048] = {0};
jboolean isCopy;
c_str = env->GetStringUTFChars(j_str, &isCopy);

if(c_str == NULL) {
return NULL;
}

sprintf(buff, "这里是C++部分编写的字符串,这里是Java 传过来的字符串:%s", c_str);
env->ReleaseStringUTFChars(j_str, c_str);
return env->NewStringUTF(buff);
}

JNIEXPORT jint JNICALL Java_org_woodwhales_king_JniTest_plus
(JNIEnv * env, jobject j_obj, jint j_x, jint j_y) {
int result = j_x + j_y;
return result;
}

源码编写完成后,点击生成菜单生成解决方案,或按Ctrl+F7

如果编码没有问题,则会在控制台输出如下信息:

1
2
3
4
1>生成成功。
1>
1>已用时间 00:00:00.13
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========

此时再次进入项目根目录,会发现在 x64 目录下的 Release 目录下,生成了 dll 文件:

步骤7. 测试dll文件

在 java 项目根目录下创建一个名称为 lib 的资源文件目录,用于存放步骤6 开发出来的 dll 文件:

将步骤 6开发出来的 dll 文件拷贝一份到这个lib目录下:

将这个 lib 目录设置为 native 依赖库:

由于 lib 已经是资源目录,所以 lib 中的文件都会被编译到项目的 bin 目录,所以指定项目的 bin 目录为 native 依赖库即可:

编写测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {

public static void main(String[] args) {
JniTest jniTest = new JniTest();

String message = jniTest.getMessage("my blog -> 个人博客:https://woodwhales.github.io/");
System.out.println(message);

int result = jniTest.plus(3, 4);
System.out.println(result);
}
}

输出结果:

这里是C++部分编写的字符串,这里是Java 传过来的字符串:my blog -> 个人博客:https://woodwhales.github.io/
7

参考资料:

Java Programming Tutorial - Java Native Interface (JNI)

JAVA之JNI从头完整实例

Java采用JNI调用VC++生成的dll(Java与C++交互)

Android高级第65课 jni开发之完美解决中文乱码方案【通俗易懂-深入底层学习Android】

JNI 字符串处理

JNI自用手册3 - 拾遗篇

(三)JNI 中文乱码

JNI乱码问题

JNI系列入门之C语言中文字符串乱码问题

JNI技术实践小结

Jni ,从入门到入坟

Android JNI学习(四)——JNI的常用方法的中文API

updated updated 2020-03-01 2020-03-01
本文结束感谢阅读

本文标题:JNI技术实战

本文作者:木鲸鱼

微信公号:木鲸鱼 | woodwhales

原始链接:https://woodwhales.cn/2019/07/07/045/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%