1.创建java对象
首先在java端自定义一个Person类如下
- package com.example;
- public class Person {
- public String name;
- public int age;
- public Person()
- {
- }
- public Person(String name,int age)
- {
- this.name=name;
- this.age=age;
- }
- public void Desc()
- {
- System.out.println("姓名: "+this.name+" 年龄:"+this.age);
- }
- }
定义本地方法sayHello方法,在sayHello方法里实现创建Person对象,并调用创建对象的Desc()方法
- package com.example;
- public class jni_test {
- //在本地方法sayHello里里创建Person对象
- public native void sayHello();
- static{
- System.loadLibrary("NativeCode");
- }
- public static void main(String[] args) {
- jni_test temp=new jni_test();
- temp.sayHello();
- }
- }
jni中创建java对象需要使用NewObject方法,jni里定义构造函数的名称为<init>,返回值为Void
1.调用默认构造函数创建Person对象
- JNIEXPORT void JNICALL Java_com_example_jni_1test_sayHello(JNIEnv * evn, jobject obj)
- {
- //获取java的Class
- jclass my_class=evn->FindClass("com/example/Person");
- //获取java的Person构造方法id---构造函数的函数名为<init>,返回值为void
- jmethodID init_id=evn->GetMethodID(my_class,"<init>","()V");//(类,属性名.签名)
- //创建Person对象--使用NewObject方法
- jobject person=evn->NewObject(my_class,init_id);
- //为person赋值
- jfieldID name_id=evn->GetFieldID(my_class,"name","Ljava/lang/String;");
- evn->SetObjectField(person,name_id,(evn)->NewStringUTF("mike"));
- jfieldID age_id=evn->GetFieldID(my_class,"age","I");
- evn->SetIntField(person,age_id,20);
- //获取Person的Desc方法id
- jmethodID desc_id=evn->GetMethodID(my_class,"Desc","()V");
- //调用创建的person里的desc方法
- evn->CallVoidMethod(person,desc_id);
- }
运行结果:
2.调用有参构造方法,构造对象时完成对象属性赋值
- JNIEXPORT void JNICALL Java_com_example_jni_1test_sayHello(JNIEnv * evn, jobject obj)
- {
- //获取java的Class
- jclass my_class=evn->FindClass("com/example/Person");
- //获取java的Person构造方法id---构造函数的函数名为<init>,返回值为void
- jmethodID init_id=evn->GetMethodID(my_class,"<init>","(Ljava/lang/String;I)V");//(类,属性名.签名)
- //创建Person对象--使用NewObject方法
- jobject person=evn->NewObject(my_class,init_id, (evn)->NewStringUTF("mike"),20);
- //获取Person的Desc方法id
- jmethodID desc_id=evn->GetMethodID(my_class,"Desc","()V");
- //调用创建的person里的desc方法
- evn->CallVoidMethod(person,desc_id);
- }
运行结果:
2.jni和java中文乱码
如果把创建Person对象改为
jobject person=evn->NewObject(my_class,init_id, (evn)->NewStringUTF("珍奇异"),20)
此时运行结果产生了中文乱码:
乱码产生原因
java内部是使用16bit的unicode编码(UTF-16)来表示字符串的,无论中文英文都是2字节;jni内部是使用UTF-8编码来表示字符串的,UTF-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
1、java --> c/c++
这种情况中,java调用的时候使用的是UTF-16编码的字符串,jvm把这个字符串传给jni,c/c++得到的输入是jstring,这个时 候,可以利用jni提供的两种函数,一个是GetStringUTFChars,这个函数将得到一个UTF-8编码的字符串;另一个是 GetStringChars这个将得到UTF-16编码的字符串。
2、c/c++ --> java
jni返回给java的字符串,c/c++首先应该负责把这个字符串变成UTF-8或者UTF-16格式,然后通过NewStringUTF或者NewString来把它封装成jstring,返回给java就可以了。
解决方法
方法一:使用#include <windows.h>头文件函数
导入头文件#include <windows.h> ,此时有利用两个函数来处理字符串
- //将jstring类型转换成windows类型
- char* jstringToWindows( JNIEnv *env, jstring jstr )
- {
- int length = (env)->GetStringLength(jstr );
- const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
- char* rtn = (char*)malloc( length*2+1 );
- int size = 0;
- size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
- if( size <= 0 )
- return NULL;
- (env)->ReleaseStringChars(jstr, jcstr );
- rtn[size] = 0;
- return rtn;
- }
- //将windows类型转换成jstring类型
- jstring WindowsTojstring( JNIEnv* env, char* str )
- {
- jstring rtn = 0;
- int slen = strlen(str);
- unsigned short * buffer = 0;
- if( slen == 0 )
- rtn = (env)->NewStringUTF(str );
- else
- {
- int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
- buffer = (unsigned short *)malloc( length*2 + 1 );
- if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
- rtn = (env)->NewString( (jchar*)buffer, length );
- }
- if( buffer )
- free( buffer );
- return rtn;
- }
此时把本地完整代码为:
- #include"com_example_jni_test.h"
- #include<iostream>
- #include <windows.h>
- using namespace std;
- //将windows类型转换成jstring类型
- jstring WindowsTojstring( JNIEnv* env, char* str )
- {
- jstring rtn = 0;
- int slen = strlen(str);
- unsigned short * buffer = 0;
- if( slen == 0 )
- rtn = (env)->NewStringUTF(str );
- else
- {
- int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
- buffer = (unsigned short *)malloc( length*2 + 1 );
- if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
- rtn = (env)->NewString( (jchar*)buffer, length );
- }
- if( buffer )
- free( buffer );
- return rtn;
- }
- JNIEXPORT void JNICALL Java_com_example_jni_1test_sayHello(JNIEnv * evn, jobject obj)
- {
- //获取java的Class
- jclass my_class=evn->FindClass("com/example/Person");
- //获取java的Person构造方法id---构造函数的函数名为<init>,返回值为void
- jmethodID init_id=evn->GetMethodID(my_class,"<init>","(Ljava/lang/String;I)V");//(类,属性名.签名)
- //创建Person对象--使用NewObject方法
- jobject person=evn->NewObject(my_class,init_id, WindowsTojstring(evn,"珍奇异"),20);
- //获取Person的Desc方法id
- jmethodID desc_id=evn->GetMethodID(my_class,"Desc","()V");
- //调用创建的person里的desc方法
- evn->CallVoidMethod(person,desc_id);
- }
运行结果:
方法二:
完整c++代码:
- #include"com_example_jni_test.h"
- #include<iostream>
- using namespace std;
- //检查是否含有中文
- int isASCII(const char * chp)
- {
- char ch;
- jboolean flag= 1;
- while(ch = *chp++){
- if(ch & 0x80){
- flag = 0;
- break;
- }
- }
- return flag;
- }
- //jstring to char*
- char* JstringToPchar(JNIEnv* env, jstring jstr, const char * encoding,jmethodID gmidStringGetBytes)
- {
- char* rtn = NULL;
- jstring jencoding;
- jencoding=(env)->NewStringUTF(encoding);
- jbyteArray barr= (jbyteArray)(env)->CallObjectMethod(jstr, gmidStringGetBytes, jencoding);
- jsize alen = (env)->GetArrayLength(barr);
- jbyte* ba = (env)->GetByteArrayElements(barr, JNI_FALSE);
- if (alen > 0)
- {
- rtn = (char*)malloc( alen + 1);
- memcpy(rtn, ba, alen);
- rtn[alen] = 0;
- }
- (env)->ReleaseByteArrayElements(barr, ba, 0);
- return rtn;
- }
- //char* to jstring
- jstring PcharToJstring(JNIEnv* env, const char* pchar,const char * encoding,jclass gStringClass,jmethodID gmidStringInit)
- {
- jstring jencoding;
- jbyteArray bytes = (env)->NewByteArray(strlen(pchar));
- env->SetByteArrayRegion(bytes, 0, strlen(pchar), (jbyte*)pchar);
- jencoding = env->NewStringUTF(encoding);
- return (jstring)(env)->NewObject(gStringClass, gmidStringInit, bytes, jencoding);
- }
- JNIEXPORT void JNICALL Java_com_example_jni_1test_sayHello(JNIEnv * evn, jobject obj)
- {
- //获取java的Class
- jclass my_class=evn->FindClass("com/example/Person");
- //获取java的String相关方法
- jclass str_class=evn->FindClass("java/lang/String");
- jmethodID Byte_id=evn->GetMethodID(str_class,"getBytes", "(Ljava/lang/String;)[B");
- jmethodID Strinit_id=evn->GetMethodID(str_class,"<init>","([BLjava/lang/String;)V");
- //生成防止乱码字符串--结果赋给result
- char *myTest = "珍奇异";
- jstring result;
- //没有中文的情况
- if(isASCII(myTest)) {
- result = evn->NewStringUTF(myTest);
- }
- else{
- result = PcharToJstring(evn,myTest,"gbk",str_class,Strinit_id);
- char *outbuf;
- outbuf = JstringToPchar(evn,result,"utf-8",Byte_id);
- result = (evn)->NewStringUTF(outbuf);
- if(outbuf){
- free(outbuf);
- }
- }
- //获取java的Person构造方法id---构造函数的函数名为<init>,返回值为void
- jmethodID init_id=evn->GetMethodID(my_class,"<init>","(Ljava/lang/String;I)V");//(类,属性名.签名)
- //创建Person对象--使用NewObject方法
- jobject person=evn->NewObject(my_class,init_id, result,20);
- //获取Person的Desc方法id
- jmethodID desc_id=evn->GetMethodID(my_class,"Desc","()V");
- //调用创建的person里的desc方法
- evn->CallVoidMethod(person,desc_id);
- }
运行结果: