Java-OS X系统下JNI步骤

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++工程

CppProject

注意,在Mac系统下,会找不到JNI文件,需要先找出JDK所在的目录,命令:

/usr/libexec/java_home -V

可以看到JDK的目录为:
CppProject

到该目录的include文件夹中,将jni.h和darwin文件夹中的jawt_md.h、jni_md.h共三个文件拷贝到工程中。
CppProject

此时,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一项。

RunScript

并在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库的路径,并运行,可看到结果。

Result

如果运行时,出现以下异常:

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库的路径不正确。