JNI 之中文乱码和数据类型访问

因为这一篇是接着上一篇的所以,环境配置什么的都不在赘述了。

一、实现

1、目录结构

2、Test.java中的native方法

	//访问构造方法
	public native long accessConstructor();
	//调用父类方法
	public native void accessNonvirtualMethod();
	//解决中文乱码
	public native String chineseChars(String in);
	//得到排序的数组
	public native void giveArray(int[] array);
	//得到指定长度的数组
	public native int[] getArray(int len);
	//局部引用
	public native void localRef();
	//创建全局引用
	public native void createglobalRef();
	//得到全局引用
	public native String getglobalRef();
	//释放全局引用
	public native void deleteglobalRef();
	//jni异常处理
	public native void exeception();
	//jni缓存策略
	public native void cache();
3、在c文件中的native方法实现

//访问构造方法,使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_com_test_Test_accessConstructor(JNIEnv * env, jobject jobj){
	jclass cls = (*env)->FindClass(env, "java/util/Date");
	//jmethodID
	jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
	//实例化一个Date对象
	jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);
	//调用getTime方法
	jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
	jlong time = (*env)->CallLongMethod(env, date_obj, mid);

	printf("\ntime:%lld\n", time);

	return date_obj;
}
//调用父类的方法
JNIEXPORT void JNICALL Java_com_test_Test_accessNonvirtualMethod(JNIEnv * env, jobject jobj){
	jclass cls = (*env)->GetObjectClass(env, jobj);
	//获取man属性(对象)
	jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/test/Human;");
	//获取
	jobject human_obj = (*env)->GetObjectField(env, jobj, fid);

	//执行sayHi方法
	jclass human_cls = (*env)->FindClass(env, "com/test/Human"); //注意:传父类的名称
	jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V");

	//执行
	//(*env)->CallObjectMethod(env, human_obj, mid);
	//调用的父类的方法
	(*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);

}

//中文乱码
JNIEXPORT jstring JNICALL Java_com_test_Test_chineseChars(JNIEnv * env, jobject jobj,jstring in){
	//输出 java->C
	char* temp_str=(*env)->GetStringUTFChars(env, in, JNI_FALSE);

	printf("in _str%s\n", temp_str);
	//C-->jstring
	char* c_str = "我有一只小毛驴";
	//jstring j_str=(*env)->NewStringUTF(env, c_str);
	/*
	执行String(byte bytes[], String charsetName)构造方法需要的条件
	1、jmethodID
	2、byte数组
	3、字符编码
	*/
	jclass str_cls = (*env)->FindClass(env, "java/lang/String");

	jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");

	//jbyte -> char 
	//jbyteArray -> char[]
	jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
	//byte数组赋值
	//0->strlen(c_str),从头到尾
	//对等于,从c_str这个字符数组,复制到bytes这个字符数组
	(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);

	//字符编码jstring
	jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

	//调用构造函数,返回编码之后的jstring
	return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}
int compare(int *a,int *b){
	return (*a)-(*b);
}
//数组排序
JNIEXPORT void JNICALL Java_com_test_Test_giveArray(JNIEnv * env, jobject jobj, jintArray  inArr){
	//jintArray的指针
	jint * elems=	(*env)->GetIntArrayElements(env, inArr, JNI_FALSE);
	//数组长度
	int len=(*env)->GetArrayLength(env, inArr);
	//排序
	qsort(elems, len, sizeof(jint),compare);
	//同步数组
	//JNI_ABORT 对java数组不进行更新,但是释放c/c++数组
	//JNI_COMMIT 对java数组进行更新,不释放c/c++数组(但函数执行完,数组还是会释放)
	(*env)->ReleaseIntArrayElements(env, inArr, elems,JNI_COMMIT);
}
//返回数组
JNIEXPORT jintArray JNICALL Java_com_test_Test_getArray(JNIEnv * env, jobject jobj, jint  len){
	//创建一个指定大小的数组
	jintArray jint_arr=(*env)->NewIntArray(env, len);
	//jboolean bool=NULL;	
	//jintArray的指针,也可以用jboolean的指针,&bool
	jint * elems = (*env)->GetIntArrayElements(env, jint_arr, JNI_FALSE);
	int i = 0;
	for (; i < len; i++)
	{
		elems[i] = i;
	}
	//数组长度
	//int len = (*env)->GetArrayLength(env, inArr);
	//排序
	//qsort(elems, len, sizeof(jint), compare);
	//同步数组
	//JNI_ABORT 对java数组不进行更新,但是释放c/c++数组
	//JNI_COMMIT 对java数组进行更新,不释放c/c++数组(但函数执行完,数组还是会释放)
	(*env)->ReleaseIntArrayElements(env, jint_arr, elems, JNI_COMMIT);

	return jint_arr;
}
//局部引用。模拟:循环创建数组
/*
1、访问一个很大的java对象,使用完之后,还要进行复杂的耗时操作
2、创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有关联性
*/
JNIEXPORT void JNICALL Java_com_test_Test_localRef(JNIEnv * env, jobject jobj){
	int i = 0;
	for (i; i < 5; i++)
	{
		jclass cls = (*env)->FindClass(env, "java/util/Date");
		//jmethodID
		jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
		jobject obj=(*env)->NewObject(env, cls, constructor_mid);

		//不在使用obj,通知垃圾回收器释放
		(*env)->DeleteLocalRef(env, obj);
		//.............
	}
}
//全局引用

jstring global_str;
/*
需要自己处理线程安全问题
*/
//创建全局引用
JNIEXPORT void JNICALL Java_com_test_Test_createglobalRef(JNIEnv * env, jobject jobj){
	jstring obj=(*env)->NewStringUTF(env, "我有一只小毛驴  ok!!!!!");
	global_str=(*env)->NewGlobalRef(env, obj);
}
//获得全局引用
JNIEXPORT jstring JNICALL Java_com_test_Test_getglobalRef(JNIEnv * env, jobject jobj){

	return global_str;
}
//释放全局引用
JNIEXPORT void JNICALL Java_com_test_Test_deleteglobalRef(JNIEnv * env, jobject jobj){
	(*env)->DeleteGlobalRef(env, global_str);
	
}
//弱全局引用
/*
1、节省内存,在内存不足时可以释放所引用的对象
2、可以引用一个不常用的对象,如果为NULL,临时创建
创建:(*env)->NewWeakGlobalRef
销毁:(*env)->DeleteWeakGlobalRef
*/
//异常处理
/*
1、保证java代码可以运行
2、补救措施保证C代码继续运行

JNI自己抛出的异常,在Java层不能被Exception捕获,但是可以被Throwable捕获,也可以在c层清空
用户通过ThrowNew抛出的异常,可以在Java层捕获
*/
JNIEXPORT void JNICALL Java_com_test_Test_exeception(JNIEnv * env, jobject jobj){
jclass cls=	(*env)->GetObjectClass(env, jobj);
jfieldID fid= (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String");
//检测是否发生java异常
jthrowable exception=(*env)->ExceptionOccurred(env);
if (exception != NULL){
	//让java代码继续运行,清空异常
	(*env)->ExceptionClear(env);
	
	//补救措施,获取别的属性
	fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String");
}
//获取属性的值
jstring jstr=(*env)->GetObjectField(env, jobj, fid);
char* str=(*env)->GetStringUTFChars(env, jstr, JNI_FALSE);
//对比属性值,是否合法

if (_stricmp(str, "super zhangsan1")!=0){
	//自己抛出异常给Java层处理
	jclass newExcCls=(*env)->FindClass(env, "java/lang/IllegalArgumentException");
	(*env)->ThrowNew(env, newExcCls, "key is value is error!");
}
}
//缓存策略
/*
1、局部静态化
2、变量全局化
*/
JNIEXPORT void JNICALL Java_com_test_Test_cache(JNIEnv * env, jobject jobj){

	jclass cls = (*env)->GetObjectClass(env, jobj);

	
	//获取jfieldID只获取一次,局部静态变量(作用域被销毁了,但是值仍然在内存中,整个程序结束,该静态变量才会被销毁)
	static jfieldID key_id = NULL;
	if (key_id==NULL){
		key_id = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String");
		printf("---------------------37>>>" );
	}
}

、Eclipse NDK开发流程

1.编写Java层Native方法
2.javah命令生成头文件
3.创建jni目录
4.添加本地支持add native support
5.实现头文件中定义的函数
6.编译生成.so动态库
7.加载动态库

注意:

1、如果在eclipse中出现问号,和没有提示,那是因为没有配置ndk环境变量

2、添加提示环境变量

该位置添加如下变量然后就会有提示,而且也不会有问号了

D:\NDK\android-ndk-r9\toolchains\arm-linux-androideabi-4.6\prebuilt\windows-x86_64\lib\gcc\arm-linux-androideabi\4.6\include


D:\NDK\android-ndk-r9\toolchains\arm-linux-androideabi-4.6\prebuilt\windows-x86_64\lib\gcc\arm-linux-androideabi\4.6\include-fixed


D:\NDK\android-ndk-r9\platforms\android-18\arch-arm\usr\include



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值