NDK开发学习(一):尝试JNI

注:此过程在mac环境下

JNI开发流程简介

  • 编写声明了 native 方法的 Java 类
  • 将 Java 源代码编译成 class 字节码文件
  • 用 javah -jni 命令生成.h头文件(javah 是 jdk 自带的一个命令,-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数)
  • 用本地代码实现.h头文件中的函数
  • 将本地代码编译成动态库(Windows:*.dll,linux/unix:*.so,mac os x:*.jnilib)
  • 拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序

尝试JNI

一.定义一个native 函数

HelloWorld.java

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld {
static {
//所需加载的动态库的完整路径 System.load("/Users/zhangyu/Desktop/JavaProjects/LearnJNI/jni/libHelloWorld.jnilib");
}
public static native String sayHello(String name);
public static void main(String... args) {
String text = sayHello("zpauly");
System.out.println(text);
}
}
二.使用将.java文件编译为.class文件
1
javac src/HelloWorld.java -d bin

这里我使用-d命令将class文件生成到指定目录下

三.根据.class文件生成.h头文件
1
javah -jni -classpath bin -d jni HelloWorld

默认生成的头文件的名称为:包名+类名.h,如:若此处HelloWorld的包名为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
javah -jni命令参数
- classpath:类搜索路径,这里表示从当前的 bin 目录下查找
- d:将生成的头文件放到当前的 jni 目录下
- o: 指定生成的头文件名称,默认以类全路径名生成(包名+类名.h)
此时生成的头文件为
> HelloWorld.h
```c++
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_HelloWorld_sayHello
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif

四.实现头文件中的函数

HelloWorld.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "HelloWorld.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_HelloWorld_sayHello(JNIEnv *env, jclass clazz, jstring j_str) {
const char *c_str = NULL;
char buff[128] = { 0 };
c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
if (c_str == NULL) {
printf("out of memory.\n");
return NULL;
}
(*env)->ReleaseStringUTFChars(env, j_str, c_str);
printf("Java String:%s\n", c_str);
sprintf(buff, "hello %s\n", c_str);
return (*env)->NewStringUTF(env, buff);
}
#ifdef __cplusplus
}
#endif
五.将写好的c/c++代码编译为本地动态库

动态库命名规则:lib+动态库文件名+后缀

1
gcc -dynamiclib -o jni/libHelloWorld.jnilib jni/HelloWorld.c -framework JavaVM -I/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/include/darwin

参数说明:

  • -dynamiclib:表示编译成动态链接库
  • -o:指定动态链接库编译后生成的路径及文件名
  • -framework JavaVM -I:编译 JNI 需要用到 JVM 的头文件(jni.h),第一个目录是平台无关的,第二个目录是与操作系统平台相关的头文件
六.运行Java程序

运行效果

NDK开发学习(一):尝试JNI_1.png