在本地方法实现代码中创建java对象,其中还主要涉及到jni和java之间中文字符串的的乱码问题。
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); }
运行结果:
相关推荐
这是我用于测试,主要用于jni创建java对象病操作
Java通过JNI和c++对象数据的传递和对象的返回
在jni中操作arraylist对象,然后添加一个int型数据进去
最近项目中需要使用JNI,所以研究了一下,其中遇到过不少问题,总结一下,让遇到同样问题的人...在C/C++中调用Java的方法一般分为五个步骤:初始化虚拟机、获取类、获取类的方法、创建类对象、调用方法和退出虚拟机。
1.C++与java交互基本介绍 2.C++获取java属性值、设置java属性值 3.C++创建java对象 4.C++中数据类型间的转换
12、实例十二:在jni中创建java类对象:... 66 Chap10:在 Windows 中实现 Java 本地方法... 66 1.Java 调用 C. 67 2.调试... 76 3.其他信息... 79 Chap11:如何在C/C++中调用Java. 80 1.环境搭建... 81 2.初始...
FreeType-jni 使用将Java绑定到库。...要初始化FreeType库,请使用FreeType类创建一个新的Library对象。 每个library都是完全独立的。 它是一组对象(如字体,外观,大小等)的“根”。 Library library = FreeType
基于PBNI和JNI的PB调用JAVA代码方案,导入项目后得到cjavavm类,支持加载JVM、在PB中创建Java对象的代理对象
实列是较全面的安卓JNI开发资料,jni访问java属性,设置属性,回调java函数,创建jni进程,新建java对象 可以从以上几个方面对jni进行系统的学习
方法签名,如果是一个非静态数据类型,就得把整个路径都写上,并且在最前面加上L,例如String类型的写法是: Ljava/lang/String创建对象(如果被
【Android NDK 开发】JNI 线程 ( JNI 线程创建 | 线程执行函数 | 非 JNI 方法获取 JNIEnv 与 Java 对象 | 线程获取 JNIEnv | 全局变量设置 ) 博客地址 : ...
Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...
Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...
中文名: Java2游戏编程 ...附录C 使用JNI创建一个gamepad阅读器 附录D 使用.JAR文件来部署Java applet 附录E 正确运行Java 2 applet 附录F Magic游戏引擎源码清单 附录G 更多的资源 附录H 配书光盘中有些什么
A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 ...
A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 ...
A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 ...
A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 ...
A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 其他J/Direct...
A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 ...