JNI使用:步步解析

虽然java用了很长的一段时间,但一直没有机会去碰JNI的东西。最近终于有项目需要用到JNI,并且我们是要用c++调用java代码。为什么我们需要这么做呢?

1. 第三方的功能比较符合我们想要的库是用java实现的;

2. 它是开源的并且能够用于商业用途;

3. 上层的应用对时间、效率又有一定的要求。

其实,上面三点需求在一定程度上来讲是相互违背的。讲究效率,那么必然用c/c++,所以,我们在能够控制的代码用c/c++,但不能控制的第三方库的实现用的是java,效率必然不会太高,但就是因为免费,我们也只能才去这种蹩脚的实现方法了。

不管怎么样,还是来个例子:

#include <iostream>
#include <string.h>
#include <jni.h>
#include <stdlib.h>

using namespace std;

#define PATH_SEPARATOR ';' /* define it to be ':' on Solaris */
#define USER_CLASSPATH1 "imported1.jar" /* where Prog.class is */
#define USER_CLASSPATH2 "imported2.jar"   // imported2.jar 引用了imported1.jar中的类Prog.Java

#define TEST1 "edu/Test1"   //edu/Test1 located in imported1.jar
#define TEST2	"edu/Test2" //edu/Test2 located in imported2.jar

JNIEnv *env;
JavaVM *jvm;
jint res;

int initJVM() {

#ifdef JNI_VERSION_1_2
 JavaVMInitArgs vm_args;
 JavaVMOption options[3];
options[0].optionString = "-Xms4096m";
options[1].optionString = "-Xmx8000m";
options[2].optionString ="-Djava.class.path=" USER_CLASSPATH1 ":" USER_CLASSPATH2; //import multiple jar file
//options[2].optionString = "-Djava.class.path = <path_to_my_java_class>:<path_to_my_jar_file>";
//options[3].optionString = "-Djava.library.path=" CLASSPATH; //The library path is the path for finding native libraries, not JAR files
 vm_args.version = 0x00010002;

 vm_args.nOptions = 3;
 vm_args.options = options;
 vm_args.ignoreUnrecognized = JNI_TRUE;
 /* Create the Java VM */
 res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else
 JDK1_1InitArgs vm_args;
 char classpath[1024];
 vm_args.version = 0x00010001;
 JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */
 sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
 vm_args.classpath = classpath;
 /* Create the Java VM */
 res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif /* JNI_VERSION_1_2 */
 //if(res==JNI_OK) cout<<"init VM Successfully!"<<endl;
 return res;
}

void closeJVM() {
    jvm->DestroyJavaVM();
    //cout<<"Destory VM Successfully!"<<endl;
}

//test default java class 
jstring JNU_NewStringNative(const char *str)
{
 jclass strClass = env->FindClass("java/lang/String");
 //if(strClass) {
//	cout<<"strClass Successfully!"<<endl;
// } 
 jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); 
 jstring encoding = env->NewStringUTF("UTF-8"); 

jbyteArray bytes = env->NewByteArray(strlen(str)); 
env->SetByteArrayRegion(bytes, 0, strlen(str), (jbyte*)str); 
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}

const char *Test2(char* input) {
  
    jclass tagEngine; 
    jobject tagObject;
    
    tagEngine = env->FindClass(TEST2);
    if(env->ExceptionCheck()) {
    	return 0;
    }
    //get construct method ID
    jmethodID initMethod = env->GetMethodID(tagEngine, "<init>", "()V");

    //get tag method ID. If Test2 class has "String tag(String s)" method, return non-NULL; or return NULL
    jmethodID tagMethod = env->GetMethodID(tagEngine, "tag", "(Ljava/lang/String;)Ljava/lang/String;");
    if(initMethod && tagMethod) {
        //new Test2 object
        tagObject = env->NewObject(tagEngine,initMethod,path);
	if(tagObject) {
            // call tag method
            jstring tagsOutput = (jstring)env->CallObjectMethod(tagObject,tagMethod,inputString);
            // exception handing
            if(env->ExceptionCheck()) return 0;
    	    const jbyte *str = (jbyte *)(env)->GetStringUTFChars(tagsOutput, NULL);
    	    return ((char*)str);
        }
    }
    
    return 0;
}



int main() {
    const char* resint;
    if(JNI_OK==initJVM()) {
    	resint = Test2("这个例子不错。");//trove_version();//
    	if(resint) 
		printf("%s\n", resint); 
    	else 
		printf("error\n");
	closeJVM();
    }
    return 0;
}

针对JNI的初始化,可以参考:JNI: API的调用
针对JNI的设计,例如异常处理、对象引用,可以参考:JNI使用:设计综述

有了代码,离成功的编译还有很远的一段距离。其实使用JNI最难的问题是JVM参数的设置。下面列出了我的编译命令:

g++ -I/usr/java/latest/include/ -I/usr/java/latest/include/linux -L/usr/java/latest/jre/lib/amd64/ -L/usr/java/latest/jre/lib/amd64/server/ -Wl,-rpath,/usr/java/latest/jre/lib/amd64/server -ljvm -lstdc++ -o JarTest JarTest.cpp

上面的一长串是在终端输入(我用的centos系统,网上大部分都是针对window系统,由于window系统不需要管JVM的路径,我花了好多时间才找到JVM的库)。

一条一条来分析:

1、我的JAVA_HOME目录为:usr/java/latest/;

2、 -I 是添加头文件,所以我引入的头文件目录为/usr/java/latest/include/ 和/usr/java/latest/include/linux;其实这里是需要<jni.h>文件。注意:openjdk是没有include目录的,一定要从oracle的官网下载java jdk

3、-L是添加库文件,注意我引入的库为:/usr/java/latest/jre/lib/amd64/ 和 /usr/java/latest/jre/lib/amd64/server/ ,其实这里需要的是libjvm.so文件。注意两点:

  • 如果没有引入这两个库文件夹,那么编译是会出现编译错误,并且error提示为:"/usr/bin/ldcannot find -lXXX",原因就是没有找到libjvm.so;
  • 只有/usr/java/latest/jre/lib/amd64/server/里面采用libjvm.so,而/usr/java/latest/jre/lib/amd64/client/里面没有。而大部分安装的java都是client版本的,所以要注意一下。

4、 Wl是什么意思,可以用g++ --help直到Wl参数的作用,注意它的参数采用","分隔;

5、-ljvm、-lstdc++、-o就不多说了。


参考文献:

关于JNI的函数调用:参考:JNI: API的调用

关于JNI的异常处理,参考:JNI使用:设计综述

官方文档:Java Native Interface Specification—Contents

Programmming in C/C++ with the Java Native Interface

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值