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代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中,数组是非常常见的数据结构,而在C/C++中,操作数组也是非常方便的。在JNI中,我们可以通过JNI提供的函数来操作Java中的数组。本篇文章将详细介绍如何在C/C++中操作Java中的数组。 1. 获取数组信息 在C/C++中,我们需要获取Java中数组的长度、元素型、以及数组元素的指针等信息才能对数组进行操作。JNI提供了一系列的函数来获取这些信息。 - 获取数组长度:使用GetArrayLength()函数可以获取Java数组的长度,其函数原型如下: ```c++ jsize GetArrayLength(JNIEnv *env, jarray array); ``` 其中,env表示JNI的环境变量,array表示要获取长度的Java数组。该函数返回Java数组的长度。 - 获取数组元素型:使用GetArrayElements()函数可以获取Java数组的元素型,其函数原型如下: ```c++ jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize); jbyteArray (*NewByteArray)(JNIEnv*, jsize); jcharArray (*NewCharArray)(JNIEnv*, jsize); jshortArray (*NewShortArray)(JNIEnv*, jsize); jintArray (*NewIntArray)(JNIEnv*, jsize); jlongArray (*NewLongArray)(JNIEnv*, jsize); jfloatArray (*NewFloatArray)(JNIEnv*, jsize); jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize); ``` 其中,env表示JNI的环境变量,size表示数组的长度。该函数返回一个新的Java数组对象。 - 获取数组元素指针:使用GetArrayElements()函数可以获取Java数组的元素指针,其函数原型如下: ```c++ jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*); jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*); jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*); jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*); jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*); jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*); jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*); ``` 其中,env表示JNI的环境变量,array表示要获取指针的Java数组,isCopy表示是否需要复制数据。该函数返回Java数组元素指针。 - 释放数组元素指针:使用ReleaseArrayElements()函数可以释放Java数组元素指针,其函数原型如下: ```c++ void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*, jint); void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint); void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray, jchar*, jint); void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray, jshort*, jint); void (*ReleaseIntArrayElements)(JNIEnv*, jintArray, jint*, jint); void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray, jlong*, jint); void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray, jfloat*, jint); void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray, jdouble*, jint); ``` 其中,env表示JNI的环境变量,array表示要释放指针的Java数组,elems表示要释放的Java数组元素指针,mode表示释放模式(0表示更新Java数组,JNI_COMMIT表示更新Java数组并释放elems,JNI_ABORT表示不更新Java数组并释放elems)。 2. 操作数组元素 在获取到Java数组的元素指针之后,我们就可以对Java数组进行操作了。在C/C++中,Java数组元素的访问方式与C/C++数组相同,我们可以使用数组下标的方式访问Java数组元素。 3. 代码示例 下面是一个简单的代码示例,展示了如何在C/C++中操作Java中的int数组: Java代码: ```java public class JNIIntArrayExample { static { System.loadLibrary("jniexample"); } public native int[] sort(int[] arr); } ``` C/C++代码: ```c++ #include <jni.h> #include <stdio.h> JNIEXPORT jintArray JNICALL Java_JNIIntArrayExample_sort(JNIEnv *env, jobject obj, jintArray arr) { // 获取数组长度 jsize len = env->GetArrayLength(arr); // 获取数组元素指针 jint *elems = env->GetIntArrayElements(arr, NULL); // 对数组元素进行操作 for (int i = 0; i < len; i++) { for (int j = i + 1; j < len; j++) { if (elems[i] > elems[j]) { jint tmp = elems[i]; elems[i] = elems[j]; elems[j] = tmp; } } } // 创建新的Java数组对象 jintArray result = env->NewIntArray(len); // 将排序后的数组拷贝到新的Java数组对象中 env->SetIntArrayRegion(result, 0, len, elems); // 释放数组元素指针 env->ReleaseIntArrayElements(arr, elems, 0); // 返回新的Java数组对象 return result; } ``` 其中,sort()函数接收一个Java中的int数组,对其进行排序,并返回一个新的排序后的Java中的int数组。在sort()函数中,我们首先通过GetArrayLength()函数获取Java数组的长度,然后通过GetIntArrayElements()函数获取Java数组的元素指针。接着,我们对Java数组元素进行操作,这里使用了冒泡排序算法。最后,我们创建一个新的Java数组对象,并通过SetIntArrayRegion()函数将排序后的数组拷贝到新的Java数组对象中。最后,我们通过ReleaseIntArrayElements()函数释放Java数组元素指针,并返回新的Java数组对象。 4. 总结 本篇文章介绍了如何在C/C++中操作Java中的数组,包括获取数组信息、操作数组元素等。在JNI中,操作Java中的数组与操作C/C++中的数组十分相似,只需要掌握好JNI提供的函数即可。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值