JNI技术——JNI传递对象和JNI传递中文字符串

JNI本地方法支持java对象的传递。本文就java如何传递对象给C++的DLL,在C++中又如何获取java对象的变量,如何调用java函数以及如何接收java传递的中文字符串问题做一个简单的描述。

假设我们需要传递如下所示对象:

package Editor;

class MyObject
{
int value1;
String value2;

MyObject() { }

MyObject(int v1, String v2)
{
value1 = v1;
value2 = v2;
}

public void setValue(String v2)
{
value2 = v2;
}
}

public class Editor
{
// 定义本地方法,可以是static也可以是非static
public native static int add(int x, int y);

// 定义本地方法,传入一个对象和一个字符串(如何传递中文)
public native static void transferObject(MyObject obj, String inStr);

static
{
// 加载动态链接库,不需要写扩展名,由JVM自动识别
System.loadLibrary("TestDLL");
}
}

在C++中即可按照如下方式实现本地方法:

#include "Editor.h"
#include "Utility.h"
#include "string"

JNIEXPORT jint JNICALL Java_Editor_Editor_add
(JNIEnv *, jclass, jint x, jint y)
{
return x + y;
}

JNIEXPORT void JNICALL Java_Editor_Editor_transferObject
(JNIEnv * pJNIEnv, jclass j, jobject jobj, jstring jsIn)
{
jclass jcls = pJNIEnv->GetObjectClass( jobj );

/**
* 获得变量和方法的定义,其中第三个参数是该变量的类型所对应的标签。
*
* boolean Z
* byte B
* char C
* short S
* int I
* long L
* float F
* double D
* void V
* object L包名/类名;
* Array [数组类型
* method (参数类型)返回类型
*/
jfieldID jfid1 = pJNIEnv->GetFieldID( jcls, "value1", "I" );
jfieldID jfid2 = pJNIEnv->GetFieldID( jcls, "value2", "Ljava/lang/String;");
jmethodID jmid = pJNIEnv->GetMethodID( jcls, "setValue", "(Ljava/lang/String;)V" );

// 获得变量的值
jint value1 = pJNIEnv->GetIntField( jobj, jfid1 );
jstring value2 = ( jstring )pJNIEnv->GetObjectField( jobj, jfid2 );

// 获得字符串,但是下面的函数会对中文输入产生乱码,需要自己手动转换
// char * pcValue2 = pJNIEnv->GetStringUTFChars ( value2, NULL );
char * pcValue2 = JStringToCharArray( pJNIEnv, value2 );
printf( "C++:value1 = %d, value2 = %s ", value1, pcValue2 );

char * pcIn = JStringToCharArray( pJNIEnv, jsIn );
printf( "C++:输入参数(inStr) = %s ", pcIn );

// 修改传入对象的成员变量
pJNIEnv->SetIntField( jobj, jfid1, 5 );

// 调用传入对象的函数修改成员变量
pJNIEnv->CallVoidMethod( jobj, jmid, pJNIEnv->NewStringUTF( "Chinese!" ) );
}

由于java中的中文字符编码为UNICODE,为宽字节字符集,而C++的中文字符编码为ANSII,为多字节字符集,需要对传入的中文参数执行转换。JStringToCharArray函数实现如下:

#include "jni.h"
#include "Windows.h"

char * JStringToCharArray(JNIEnv * pJNIEnv, jstring jstr)
{
jsize len = pJNIEnv->GetStringLength( jstr );
const jchar * jcstr = pJNIEnv->GetStringChars( jstr, NULL );

int size = 0;
char * str = ( char * )malloc( len * 2 + 1 );
if ( (size = WideCharToMultiByte( CP_ACP, 0, LPCWSTR( jcstr ), len, str, len * 2 + 1, NULL, NULL ) ) == 0 )
return NULL;

pJNIEnv->ReleaseStringChars( jstr, jcstr );

str[ size ] = 0;
return str;
}
测试代码:

package Editor;

public class TestEditor
{
public static void main(String[] args)
{
int ret = Editor.add(10, 10);
System.out.println("add(10, 10) = " + ret);

MyObject obj = new MyObject(10, "测试test");

System.out.println("调用DLL前:obj.value1 = " + obj.value1 + ", obj.value2 = " + obj.value2);

Editor.transferObject(obj, "测试test");

System.out.println("调用DLL后:obj.value1 = " + obj.value1 + ", obj.value2 = " + obj.value2);
}
}


运行后输出:

add(10, 10) = 20
调用DLL前:obj.value1 = 10, obj.value2 = 测试test
调用DLL后:obj.value1 = 5, obj.value2 = Chinese!
C++输出:value1 = 10, value2 = 测试test
C++输出:这是一个传入参数(inStr) = 测试test

由此可见,C++不仅能接收java对象、java字符串、也能调用java函数。如果合理的利用JNI,C++还能接收java数组、返回java对象、返回java数组。。。

你可能感兴趣的:(jni)