JNI与NDK入门之三
概述
前面两篇文章的示例代码的主程序都是用 Java 代码编写的,我们看到了如何在 Java 代码中使用 JNI 的方式调用 C 函数。下面我们将学习如何在由 C/C++ 编写的主程序中来运行 Java 类。
我们知道由 Java 类编译生成的字节码需要运行在 Java 虚拟机上,那么在 C/C++ 中运行 Java 代码也需要虚拟机环境吗?答案是肯定的。JNI 为这种情况提供了一套 Invocation API ,它允许本地代码在自身内存区域内加载 Java 虚拟机。编写并运行 Java 代码的 java
命令是由 C 语言编写的,它也是通过 Invocation API
来接收命令参数。
除此之外, Android 系统的 dalvikvm
虚拟机的主入口( dalvikvm-dalvik/dalvikvm/main.c
)也是通过 Invocation API 进行工作的。在 Android 启动时, app_process
调用 JNI invocation API
在自身程序域内加载 dalvikvm
虚拟机,而后调用 ZygoteInit
类的 main()
方法,从而运行 Zygote
进程。
使用情形
- 需要在 C/C++ 中使用标准 Java 类库。
- 需要在 C/C++ 中访问 Java 编写的代码。
功能描述
我们下面要实现一个从 C 层调用 Java 层函数的简单程序,调用流程如下图:
- 主程序
invocationApi.c
使用 Invocation API 加载虚拟机。 - 之后通过 JNI 函数加载
InvocationTest
类。 - 执行被加载类的
main()
方法,并传参。
编写 Java 代码
1 | public class InvocationTest { |
该 Java 类只有一个简单的 main()
函数,它是一个静态方法,接收的参数是一个字符串对象数组,在方法体内有且仅有一条输出语句,用来在控制台打印字符串数组的第一个数组元素。
编写 C 代码
1 | // 包含各种 JNI 必须的变量及函数 |
#include
命令将jni.h
头文件包含到本文件中。该头文件包含了 C 代码使用 JNI 必须的变量类型与函数定义。- 前面配置一些虚拟机的运行参数,参数相关内容可参考官方文档。
编译及运行
首先编译 Java 代码,如下:
1 | javac InvocaitonTest.java |
接着编译 C 代码,这里需要除了指定头文件 <jni.h>
与 <jni_md.h>
外,还需要指定 jre
的路径(注意不要使用 $JAVA_HOME/Contents/Home/jre/lib/server/ 及其配对参数 -ljvm
,否则会提示 Java 版本过低)。
1 | gcc "-I/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/include" \ |
在运行前,指定 libjli.dylib
路径:
1 | export LD_LIBRARY_PATH=/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/lib/jli/ |
结果如下: