JNI学习笔记(三):C/C++ 调用 java类 方法

本文使用的开发环境:

  1. vs2008
  2. idea 64位
  3. 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;
}



操作流程:

  1. 分别编译java类 TestA 和 javaMain 生成两个对应的 class文件,java方面的操作解结束了。
  2. 在vs 中创建 win32控制台程序 (控制台最方便测试)
  3. 将1步中生成的两个class文件拷贝到2步创建的工程目录下。(或者在c++中代码设置javaclass路径)
  4. vs项目 由于依赖jvm 需要添加jvm.lib 路径 (或者动态加载jvm.dll)
  5. vs项目 添加 jvm.dll 的路径添加到 环境中 (项目右键->属性->调试->环境 添加 PATH=xxxxx 其中xxxx是jvm所在路径)
  6. 编译vs项目 ,完毕。



注意事项:

  1. 如果电脑中存在多个jdk,注意在java编译时和 c++调用动态库时,使用同一个。
  2. 注意统一位数 ,vs项目生成程序的位数要与,jvm.dll 的位数(jdk位数 )相同,即如果jdk是32位的,vs就生成32位程序,否则生成64位程序。
  3. 如果提示丢失jvm.dll 说明 操作流程中第5步设置错了,(path后面不要空格)。
  4. c++代码中所有 jclass,jmethodID ,jfieldID 等都需要判断是否为0,测试代码中省略了。


本文中用到的jni相关函数等已经在第二篇文章中详细说明了,这里不再赘述。唯一的区别就是本文中的环境对象 env是自己创建的,第二篇文章中的env是函数传参进来的。





参考文章:
使用JNI进行Java与C/C++语言混合编程(2)–在C/C++中调用Java代码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值