JNI学习笔记(二):各种类型参数的处理和输入输出 ( String StringBuffer Arraylist 自定义类 等)

本文使用的开发环境:

  1. vs2008
  2. idea 64位
  3. jdk 1.8 64位

JAVA 调用 C/C++ 动态库

前一篇文章 JNI学习笔记(一):环境配置及简单使用 java调用c++ dll库
中已经实现了java调用dll 。 但是,由于java和c++ 的数据类型是不同的,所以下一步需要实现java 和 c++
各种类型的转换。



1.基本类型的处理

jni.h中已经对基本类型有了定义,不需要进行额外处理
示例代码

//JniTest.java  在java中的函数声明
//通过返回值 返回 int类型 a+b
public native int  intAddTest(int a,int b);

//JniTest.h    在c++中的函数声明
JNIEXPORT jint JNICALL Java_JniTest_intAddTest(JNIEnv *, jobject, jint, jint);

//JniTest.cpp 在c++中的函数实现
//intAddTest
JNIEXPORT jint JNICALL Java_JniTest_intAddTest(JNIEnv *, jobject, jint a, jint b)
{
	return a+b;
}


2.数组的处理

相关函数说明:

	//获取byte数组内的数据
	jbyte * GetByteArrayElements(
		jbyteArray array, 		// 原数组
		jboolean *isCopy		//[出参]用于返回是否成功,如果不关心是否成功,可以直接设置为NULL(0)
		)
	//返回值是一个c/c++中的数组指针

jni.h中对于每一种基础数据类型,都对应的相关函数,例如GetIntArrayElements,GetDoubleArrayElements等。

示例代码

//JniTest.java  在java中的函数声明
//通过返回值 返回 int类型 a+b
public native byte[] byteArrayTest(byte[] baA,byte[] baB);

//JniTest.h    在c++中的函数声明
JNIEXPORT jbyteArray JNICALL Java_JniTest_byteArrayTest
	(JNIEnv *, jobject, jbyteArray, jbyteArray);

//JniTest.cpp 在c++中的函数实现
//byteArrayTest
JNIEXPORT jbyteArray JNICALL Java_JniTest_byteArrayTest
	(JNIEnv *env, jobject, jbyteArray ba1, jbyteArray ba2)
{
	//获取byte数组 数据
	jbyte* buffer1 = env->GetByteArrayElements(ba1,0);
	//获取byte数组长度
	jsize ulb1Len = env->GetArrayLength(ba1);

	jbyte* buffer2 = env->GetByteArrayElements(ba2,0);
	jsize ulb2Len = env->GetArrayLength(ba2);

	//创建新内存用于存储拼接到一起的数组
	unsigned char* buffer3 = new unsigned char[ulb1Len + ulb2Len];
	ZeroMemory(buffer3,ulb1Len+ulb2Len );
	memcpy(buffer3,buffer1,ulb1Len);
	memcpy(buffer3+ulb1Len ,buffer2,ulb2Len);

	//创建新byte数组对象,用于返回
	jbyteArray rByteArray = env->NewByteArray(ulb1Len + ulb2Len);

	//用buffer3填充新键的byte数组对象
	env->SetByteArrayRegion(rByteArray,0,ulb1Len + ulb2Len,(jbyte*)buffer3);
	delete[] buffer3;

	//释放由GetByteArrayElements创建的内存
	env->ReleaseByteArrayElements(ba1,(jbyte*)buffer1,0);
	env->ReleaseByteArrayElements(ba2,(jbyte*)buffer2,0);

	return rByteArray;
}

注意事项: 函数返回前 要用ReleaseByteArrayElements 释放 GetByteArrayElements申请的内存。

3.String

GetStringChars的使用和 数组的GetxxArrayElements 类似。

需要关注的是 :

在JAVA中 字符串编码格式是UTF-8 ,而c/c++ 的编码格式 是 ANSI(中文系统下是GBK/GB18030)和 UNICODE (UTF-16),处理中文时需要转码。
jni提供了两组字符串相关的函数 : 一组接收/返回的是 UNICODE(UTF-16)字符 ,类似 GetStringCharsNewString 等, 还有一组接收/返回的是UTF-8字符,类似 GetStringUTFCharsNewStringUTF 等。 由于c/c++中没有提供UTF-8字符类型,需要手动转码,所以在使用中文字符时,使用UNICODE来处理更加方便。
示例代码中使用的全部是UNICODE,如果需要使用UTF-8,则要用WideCharToMultiByteMultiByteToWideChar进行转码。

示例代码:

//JniTest.java  在java中的函数声明
//通过返回值返回 String类型的 a + b + c
public native String strAddTest(String a,String b,String c);

//JniTest.h    在c++中的函数声明
JNIEXPORT jstring JNICALL Java_JniTest_strAddTest
  (JNIEnv *, jobject, jstring, jstring, jstring);

//JniTest.cpp 在c++中的函数实现
//strAddTest
JNIEXPORT jstring JNICALL Java_JniTest_strAddTest
	(JNIEnv *env, jobject, jstring strA, jstring strB,jstring strC)
{
	
	//由java String类对象 提取c字符串指针
	jboolean jbIsCopy = JNI_FALSE;
	const jchar* str1= env->GetStringChars(strA, &jbIsCopy);
	const jchar* str2= env->GetStringChars(strB, &jbIsCopy);
	const jchar* str3= env->GetStringChars(strC, &jbIsCopy);

	//创建、编辑新字符串以供生成新String对象
	wchar_t* pszAddStr = new wchar_t[wcslen((wchar_t*)str1) + wcslen((wchar_t*)str2) + 16];
	wcscpy(pszAddStr,(wchar_t*)str1);
	wcscat(pszAddStr,L" ");
	wcscat(pszAddStr,(wchar_t*)str2);
	wcscat(pszAddStr,L" ");
	wcscat(pszAddStr,(wchar_t*)str3);
	
	//释放由GetStringChars创建的内存
	env->ReleaseStringChars(strA,str1);
	env->ReleaseStringChars(strB,str2);
	env->ReleaseStringChars(strC,str3);
	
	//创建java String对象
	jstring rString = env->NewString((jchar*)pszAddStr,(jsize)wcslen(pszAddStr));
	delete[] pszAddStr;
	pszAddStr = NULL;

	return rString;
}


4.StringBuffer 和 ArrayList 为代表的java类

StringBuffer和ArrayList等类提供了相关方法,所以可以通过入参返回。
大致操作流程:

(1).通过GetObjectClass 或FindClass 获取java类
(2).用GetMethodID 获取类方法的id
(3).CallxxMethod 调用java方法

相关函数说明:

    jmethodID GetMethodID(	jclass clazz, 			//由GetObjectClass 或 FindClass 返回的java类
    						const char *name,		//方法名称
                          	const char *sig)		//方法签名  形式为:  (参数1类型参数2类型...)返回类型
   //函数返回值是方法id   

	jobject CallObjectMethod(jobject obj, 		//要操作的java对象
							jmethodID methodID, //GetMethodID返回的 方法id
							...)  				//对应方法的参数列表
	//这是一组函数,CallxxMethod ,其中xx是函数的返回值类型,
	//每种返回值类型都有其对应的函数,如CallBooleanMethod,CallByteMethod,CallIntMethod等

示例代码:

StringBuffer:

//JniTest.java  在java中的函数声明
//传入空字符串,返回数据通过入参strBuf返回
public native void strbufTest(StringBuffer strBuf);

//JniTest.h    在c++中的函数声明
JNIEXPORT void JNICALL Java_JniTest_strbufTest
  (JNIEnv *, jobject, jobject);

//JniTest.cpp 在c++中的函数实现
//strbufTest
JNIEXPORT void JNICALL Java_JniTest_strbufTest
(JNIEnv *env, jobject, jobject strbuf)
{
	//获取java StringBuffer类
	jclass cls_sb = env->GetObjectClass(strbuf);

	//获取StringBuffer的 setLength 和 add方法
	jmethodID mid_setLength = env->GetMethodID(cls_sb,"setLength","(I)V");
	jmethodID mid_append = env->GetMethodID(cls_sb,"append","(Ljava/lang/String;)Ljava/lang/StringBuffer;");

	//创建java String对象
	wchar_t pwstr[128] = L"这是StringBuffer入参返回的字符串";
	jstring rstring = env->NewString((jchar*)pwstr,(jsize)wcslen(pwstr));

	//调用StringBuffer 的 setLength方法,先清空
	env->CallVoidMethod (strbuf, mid_setLength, 0);
	
	//调用StringBuffer 的 append方法
	env->CallObjectMethod (strbuf, mid_append, rstring);
	
	return;
}

ArrayList:

//JniTest.java  在java中的函数声明
//通过入参slist 返回
public native void slistTest(ArrayList<String> slist);

//JniTest.h    在c++中的函数声明
JNIEXPORT void JNICALL Java_JniTest_slistTest
  (JNIEnv *, jobject, jobject);

//JniTest.cpp 在c++中的函数实现
//slistTest
JNIEXPORT void JNICALL Java_JniTest_slistTest
	(JNIEnv *env, jobject, jobject slist)
{
	//获取java ArrayList类
	jclass cls = env->GetObjectClass(slist);

	//获取ArrayList 的 add方法
	jmethodID mid = env->GetMethodID (cls, "add", "(Ljava/lang/Object;)Z");

	//创建java String对象
	wchar_t pwstr[128] = L"这是 ArrayList入参返回的字符串";
	jstring rstring = env->NewString((jchar*)pwstr,(jsize)wcslen(pwstr));
	
	//调用ArrayList 的 add方法
	env->CallObjectMethod (slist, mid, rstring);

	return;
}

5.自定义JAVA类

相关函数说明:

	//获取类成员id
    jfieldID GetFieldID(jclass clazz, 		//类
    					const char *name,	//成员名称
                        const char *sig) ;	//成员类型
	//返回值是类成员id

	//获取类对象 成员的数据
	jint GetIntField(jobject obj, 		//类对象
					jfieldID fieldID);	//由GetFieldID返回的成员id
	
	//这是一组函数,GetxxField,xx是成员类型,
	每种成员类型都有其对应的函数,如GetObjectField、GetIntField、GetCharField等

    //设置对象成员的数据,与GetIntField相对
    void SetIntField(jobject obj, 
    				jfieldID fieldID,
                     jint val) ;
	//这是一组函数,SetxxField,xx是成员类型

自定义java类TestA定义如下 :

class TestA{
    public int  A;
    public String C;
}

示例代码:

//JniTest.java  在java中的函数声明
//读取a的值,修改后,通过返回值返回ArrayList
public native ArrayList<TestA> talistTest(TestA a);

//JniTest.h    在c++中的函数声明
JNIEXPORT jobject JNICALL Java_JniTest_talistTest
  (JNIEnv *, jobject, jobject);

//JniTest.cpp 在c++中的函数实现
JNIEXPORT jobject JNICALL Java_JniTest_talistTest
(JNIEnv *env , jobject, jobject ta)
{
	//获取 java自定义类 “TestA”, 
	//		这里也可以用env->GetObjectClass(ta)获取
	jclass cls_ta = env->FindClass("TestA"); 
	jmethodID mid_testa_construct = env->GetMethodID(cls_ta,"<init>","()V");

	//获取TestA类的成员变量A 和 C
	jfieldID fid_A = env->GetFieldID(cls_ta,"A","I");  
	jfieldID fid_C = env->GetFieldID(cls_ta,"C","Ljava/lang/String;");
	
	//获取入参的 java类对象成员数据

	//获得ta.A
	int in_ta_A= env->GetIntField(ta, fid_A );  

	//获得ta.C,,jstring继承jobject 返回值强制转换成jstring
	jstring in_ta_C = (jstring)env->GetObjectField(ta,fid_C);

	//获取java  ArrayList类
	jclass cls_alist = env->FindClass("java/util/ArrayList");
	//获取 ArrayList的构造函数 和add方法
	jmethodID mid_alist_construct = env->GetMethodID(cls_alist,"<init>","()V");
	jmethodID mid_alist_add = env->GetMethodID (cls_alist, "add", "(Ljava/lang/Object;)Z");

	//编辑字符串

	//由java String类对象 提取c字符串指针
	jboolean jbIsCopy = JNI_FALSE;
	const jchar* str_taC= env->GetStringChars(in_ta_C, &jbIsCopy);
	
	//创建、编辑新字符串以供生成新String对象
	wchar_t* pwtaC = new wchar_t[wcslen((wchar_t*)str_taC) + 16];
	ZeroMemory(pwtaC,wcslen((wchar_t*)str_taC) + 16);
	wcscpy(pwtaC,(wchar_t*)str_taC);
	wcscat(pwtaC,L" 测试");
	
	//释放由GetStringChars 创建的内存
	env->ReleaseStringChars(in_ta_C,str_taC);

	//创建新的TestA对象
	jobject obj_ta = env->NewObject(cls_ta,mid_testa_construct);
	//设置TestA对象的 成员 A
	env->SetIntField(obj_ta,fid_A,in_ta_A + 1);

	//创建java String对象
	jstring rjs_ta_C = env->NewString((jchar*)pwtaC,(jsize)wcslen(pwtaC));
	delete[] pwtaC;
	pwtaC = NULL;

	//把TestA对象的 成员 C  设置成刚生成的字符串
	env->SetObjectField(obj_ta,fid_C,rjs_ta_C);

	//创建ArrayList对象
	jobject obj_alist = env->NewObject(cls_alist,mid_alist_construct);
	env->CallObjectMethod(obj_alist,mid_alist_add,obj_ta);

	return obj_alist;
}


注意事项:

  1. C/C++ 的 .h文件是由 javah 生成的,一般不要手动修改。
  2. FindClass 如果要找到类在包中,需要写包名。
  3. GetMethodID 一定注意函数签名要正确。
  4. FindClass 、GetMethodID、GetFieldID等函数需要判断返回值是否为0,示例代码中未添加判断。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值