JNI 全称为 Java Native Interface,它提供了实现Java与其他语言通信的API。注意,JNI可能会导致丧失平台可移植性。下面是调用JNI的步骤。
构建Java类
public class Main {
static {
System.load("/Users/YI/Documents/Worksplace/SICILY/Products/KeyboardService.jnilib");
}
public native ArrayList<String> search(String key);
public static void main(String[] args)
{
ArrayList<String> results = new Main().search("keyi");
for(String s : results){
System.out.println(s);
}
}
}
System.load的参数是后面编译生成后的jnilib库的目录。
search方法就是需要使用C++语言实现的方法定义。
编译Java类
javac Main.java
javah -jni Main
编译完成后,在目录下将生成Main.h文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Main
* Method: search
* Signature: (Ljava/lang/String;)Ljava/util/ArrayList;
*/
JNIEXPORT jobject JNICALL Java_Main_search
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
可见,Java_Main_search即是我们要实现的方法。
构建C++工程
注意,在Mac系统下,会找不到JNI文件,需要先找出JDK所在的目录,命令:
/usr/libexec/java_home -V
可以看到JDK的目录为:
到该目录的include文件夹中,将jni.h和darwin文件夹中的jawt_md.h、jni_md.h共三个文件拷贝到工程中。
此时,Main.h会报错,修改一下即可。
#include <jni.h> 改成 #include "jni.h"
实现Java_Main_search方法
JNIEXPORT jobject JNICALL Java_Main_search(JNIEnv *env, jobject obj, jstring str)
{
init();
search(jstring2String(env, str));
jclass clazz = env->FindClass("java/util/ArrayList");
jobject list = env->NewObject(clazz, env->GetMethodID(clazz, "<init>", "()V"));
for (int i = 0; i < results.size(); ++i)
{
jstring tmpStr = env->NewStringUTF(results[i].c_str());
env->CallObjectMethod(list, env->GetMethodID(clazz,"add","(Ljava/lang/Object;)Z"), tmpStr);
}
return list;
}
其中,JNI的对象构造和字符串转换参考:Java-JNI对象构造和字符串转换。
编写自动编译脚本
在Xcode > Target > BuildPhases面板中,增加Run Script一项。
并在RunSctipt中输入以下脚本:
INSTALL_DIR=${SRCROOT}/Products/
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cd "${INSTALL_DIR}"
g++ -o KeyboardService.o -c ${SRCROOT}/SICILY/ KeyboardService.cpp
g++ -dynamiclib -o KeyboardService.jnilib KeyboardService.o /Applications/Xcode.app/Contents/ Developer/Platforms/MacOSX.platform/Developer/SDKs/ MacOSX10.11.sdk/usr/lib/libsqlite3.tbd
open "${INSTALL_DIR}"
脚本先在项目下新建Products目录,将生成后的dynamiclib库放到目录中,并打开该目录。(由于项目中用到了sqlite库,所以将该库一同压到dynamiclib包中。)
运行
修改1中的dynamiclib库的路径,并运行,可看到结果。
如果运行时,出现以下异常:
Exception in thread "main"
java.lang.UnsatisfiedLinkError: no HelloWorld in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1758)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1045)
at HelloWorld.(HelloWorld.java:7)
则是dynamiclib库的路径不正确。