本文使用的开发环境:
- vs2008
- idea 64位
- jdk 1.8 64位
概述:
前两篇文章中已经实现了java调用 c++ 方法 ,本篇文章主要 实现 c/c++ 调用 java方法
相关文章索引:
JNI学习笔记(一):环境配置及简单使用 java调用c++
dll库
JNI学习笔记(二):各种类型参数的处理和输入输出 ( String StringBuffer Arraylist 自定义类
等)
示例代码:
自定义类TestA,用来存储数据
//TestA.java
public class TestA{
public TestA(int a,String c){
A = a;
C = c;
}
public int A;
public String C;
}
主测试类javaMain ,提供c++ 调用的方法
//javaMain.java
import java.util.ArrayList;
public class javaMain {
private int init_A;
private String init_C;
//静态方法
public static int PrintTest(int i){
System.out.println(i);
return i + 100;
}
//非静态方法
public TestA ParamTest(ArrayList<TestA> talist){
StringBuffer sb = new StringBuffer();
sb.append(init_C);
int total = init_A;
for(TestA a:talist){
sb.append(" ");
sb.append(a.C);
total += a.A;
}
return new TestA(total,sb.toString());
}
}
c++ 测试代码 ,用于创建控制台程序
// CallJava.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <jni.h>
#include <memory>
#include <locale.h>
#pragma comment(lib,"jvm.lib")
int _tmain(int argc, _TCHAR* argv[])
{
//虚拟机初始化
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
//设置class路径 ,此处. 设置为同目录
options[0].optionString = "-Djava.class.path=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
// 启动虚拟机
long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status == JNI_ERR){
return -1;
}
//静态方法测试
//获取javamain类
jclass cls_javaMain = env->FindClass( "javaMain");
// 获取方法ID
jmethodID mid_voidest = env->GetStaticMethodID(cls_javaMain, "PrintTest", "(I)I");
//调用静态方法,参数是 5
int rv = env->CallStaticIntMethod(cls_javaMain,mid_voidest,5);
printf("static func return : %d\n",rv);
//非静态方法测试
//获取方法ID
//javamain类初始化方法
jmethodID mid_maininit = env->GetMethodID(cls_javaMain,"<init>","()V");
//测试方法ParamTest
jmethodID mid_paramTest = env->GetMethodID(cls_javaMain, "ParamTest", "(Ljava/util/ArrayList;)LTestA;");
//创建javamain类对象
jobject mainObj = env->NewObject(cls_javaMain,mid_maininit);
// 获取属性ID
jfieldID fid_initA = env->GetFieldID(cls_javaMain, "init_A", "I");
jfieldID fid_initC = env->GetFieldID(cls_javaMain, "init_C", "Ljava/lang/String;");
//javamain类对象设置初始值
env->SetIntField(mainObj,fid_initA,1000);
wchar_t* pwinit = L"hello , 这是初始字符串";
//创建字符串,如果有中文字符,要用NewString + wchar_t
jstring jsC = env->NewString((jchar*)pwinit,wcslen(pwinit));
env->SetObjectField(mainObj,fid_initC,jsC);
//创建ArrayList对象
jclass cls_arraylist = env->FindClass("java/util/ArrayList");
jmethodID mid_alinit = env->GetMethodID(cls_arraylist,"<init>","()V");
jobject alObj = env->NewObject(cls_arraylist,mid_alinit);
//创建TestA对象
//获取TestA类
jclass cls_testA = env->FindClass( "TestA");
//TestA带参数初始化方法
jmethodID mid_testAinit = env->GetMethodID(cls_testA,"<init>","(ILjava/lang/String;)V");
char* pszTestaString = "hello , this is TestA.C";
//创建字符串,如果没有中文字符,可以用 NewStringUTF + char
jstring jsTaC = env->NewStringUTF(pszTestaString);
//创建TestA 对象
jobject taObj = env->NewObject(cls_testA,mid_testAinit,100,jsTaC);
//将testA对象 加到arraylsit中
jmethodID mid_alist_add = env->GetMethodID (cls_arraylist, "add", "(Ljava/lang/Object;)Z");
env->CallVoidMethod(alObj,mid_alist_add,taObj);
//调用javamain类方法 ParamTest
jobject rvObj = env->CallObjectMethod(mainObj,mid_paramTest,alObj);
int rvA = env->GetIntField(rvObj,fid_initA);
jstring rvC = (jstring)env->GetObjectField(rvObj,fid_initC);
const jchar* rvCstr= env->GetStringChars(rvC,0);
setlocale(LC_ALL, "CHS");
wprintf(L"return A : %d\nreturn C: %ls\n",rvA,rvCstr);
env->ReleaseStringChars(rvC,rvCstr);
//释放虚拟机
jvm->DestroyJavaVM();
return 0;
}
操作流程:
- 分别编译java类 TestA 和 javaMain 生成两个对应的 class文件,java方面的操作解结束了。
- 在vs 中创建 win32控制台程序 (控制台最方便测试)
- 将1步中生成的两个class文件拷贝到2步创建的工程目录下。(或者在c++中代码设置javaclass路径)
- vs项目 由于依赖jvm 需要添加jvm.lib 路径 (或者动态加载jvm.dll)
- vs项目 添加 jvm.dll 的路径添加到 环境中 (项目右键->属性->调试->环境 添加 PATH=xxxxx 其中xxxx是jvm所在路径)
- 编译vs项目 ,完毕。
注意事项:
- 如果电脑中存在多个jdk,注意在java编译时和 c++调用动态库时,使用同一个。
- 注意统一位数 ,vs项目生成程序的位数要与,jvm.dll 的位数(jdk位数 )相同,即如果jdk是32位的,vs就生成32位程序,否则生成64位程序。
- 如果提示丢失jvm.dll 说明 操作流程中第5步设置错了,(path后面不要空格)。
- c++代码中所有 jclass,jmethodID ,jfieldID 等都需要判断是否为0,测试代码中省略了。