C++中调用Java方法

背景:需要在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

上图中红色框就是需要填写数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值