背景:需要在Qt中进行Java方法的调用,就查看了一下c++如何调用Java方法,在此记录。
本文主要参考:https://www.cnblogs.com/andyliu1988/p/6041542.html
https://blog.csdn.net/wjakl001/article/details/80091810
https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
一、开发环境配置
1.1、安装JDK。
这个可以从网上搜索一下,有很多。我安装的是JDK11
1.2、配置c++的编译环境
在这一步,我使用的c++编译器是MinGW 7.3 64-bit。因为我用的是Qt进行开发,所以我直接将jdk安装路径中的include文件夹下的文件(主要是jni.h等.h文件)拷贝到了项目目录下,同时也需要将include/win32/jni_md.h拷贝此目录下,即拷贝到c++代码同一文件夹下。
二、Java代码
package practice;
public class TestLink {
public String appendQuotation(String str){
return "<"+str+">";
}
public static int add(int a,int b){
return a+b;
}
}
注意要将此类打包为jar后,再使用c++调用。
三、c++代码
#ifndef LINKJAVA_H
#define LINKJAVA_H
#endif // LINKJAVA_H
#include<jni.h>
#include<windows.h>
#include<iostream>
using namespace std;
class LinkJava{
public :
void transform();
LinkJava();
~LinkJava();
private:
JavaVMInitArgs vm_args;
JavaVMOption options[3];
JavaVM *jvm;//指向虚拟机
JNIEnv *env;//指向虚拟机环境
HINSTANCE hInstance ;
void init();
};
#include"LinkJava.h"
#include <QDebug>
LinkJava::LinkJava(){
init();
}
LinkJava::~LinkJava(){
jvm->DestroyJavaVM();
::FreeLibrary(hInstance);
}
void LinkJava::init(){
options[0].optionString="-Djava.compiler=NONE";
//指定classpath,如果需要使用自己的类(或第三方jar包),先打包成jar包,然后添加jar包路径,并以分号隔开
options[1].optionString="-Djava.class.path=.;D:\\QTproject\\Test\\myTest.jar";
//设置显示消息的类型
options[2].optionString="-verbose:jni";
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
typedef jint (WINAPI *PFunCreateJAVAVM)(JavaVM **,void **,void *);
//加载JVM.DLL动态库,必须是在安装的jdk的路径中,不能将jvm.dll拷贝到自己文件夹下,再指定
hInstance = ::LoadLibraryA("D:\\jdk11\\bin\\server\\jvm.dll");
if(hInstance==NULL){
std::cout<<"hInstance is null"<<endl;
return;
}
PFunCreateJAVAVM funCreateJavaVM = (PFunCreateJAVAVM)::GetProcAddress(hInstance,"JNI_CreateJavaVM");
int res = (*funCreateJavaVM)(&jvm, (void**)&env, &vm_args);
//报错,提示找不到此方法
// int res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if(res<0){
std::cout<<"fail"<<endl;
}else{
std::cout<<"success"<<endl;
}
}
void LinkJava::generateAPI(){
}
/**
* @brief LinkJava::transformStatic
* 调用静态方法
*/
void LinkJava::transformStatic(){
//java/lang/String
//查找指定的类
jclass cls = env->FindClass("practice/TestLink");
if(cls==NULL){
std::cout<<"not find class"<<endl;
}
jmethodID mid = env->GetStaticMethodID(cls,"add","(II)I");
jint res = env->CallStaticIntMethod(cls,mid,1,2);
std::cout<<"res: "<<res<<endl;
}
/**
* @brief LinkJava::transformObject
* 调用普通的方法
*/
void LinkJava::transformObject(){
jclass cls = env->FindClass("practice/TestLink");
if(cls==NULL){
return;
}
jobject obj = env->AllocObject(cls);
jmethodID mid = env->GetMethodID(cls,"appendQuotation","(Ljava/lang/String;)Ljava/lang/String;");
const char* cc = "中文abc123";
jstring arg = LinkJava::charTojstring2(env,cc);
jstring msg = (jstring)env->CallObjectMethod(obj,mid,arg);
string str = LinkJava::JStringToCString(env,msg);
cout<<str<<endl;
}
/**
* @brief LinkJava::JStringToCString
* @param env
* @param jstr
* @return
* 将jstring转化为string类型
*/
string LinkJava::JStringToCString(JNIEnv *env, jstring jstr){
int len = env->GetStringLength(jstr);
const jchar *jc = env->GetStringChars(jstr,0);
char *rtn = new char[len*2+1];
int size = 0;
size = WideCharToMultiByte(CP_ACP,0,(LPCWCH)jc,len,rtn,len*2+1,NULL,NULL);
if(size<0){
return NULL;
}
env->ReleaseStringChars(jstr,jc);
rtn[size] = 0;
return rtn;
}
/**
* @brief LinkJava::charTojstring2
* @param env
* @param pat
* @return
* 将包含中文的字符串转化为jstring
*/
jstring LinkJava::charTojstring2(JNIEnv* env, const char* pat) {
jclass strClass = (env)->FindClass("Ljava/lang/String;");
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);
jstring encoding = (env)->NewStringUTF("UTF-8");
return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}
注意:在调用FindClass方法时,如果可以成功找到系统类“java/lang/String”,但是找不到自己写的类,那很可能是上面配置的classPath错了。
对于函数”GetStaticMethodID“或“GetMethodID”的最后一个参数,即方法的描述,我们可以根据规则填写。如果不知道规则,可以使用javap -verbose xxxx.class,进行查看,然后填写。
例如,查看上面的TestLink中的2方法的描述:
javap -verbose TestLink.class
上图中红色框就是需要填写数据