码迷,mamicode.com
首页 > 其他好文 > 详细

【再探JNI__2】

时间:2015-04-04 18:02:30      阅读:115      评论:0      收藏:0      [点我收藏+]

标签:

昨天,我写了一下JNI的基本组成和功能。今天继续完善以下几个部分

  0:关于实例引用和类引用(昨天遗留的关于第二个参数的问题)

其实这个有点想太多了。今天验证了一下,写了两个不同的native方法。

//Demo.java
class Demo { //略... private static native void StaticMethod(String userInput);//静态方法 private native void InstaceMethod();//实例方法
public static void main(String args[]) { //省略... } }

 对应编译出来的头文件是这样的

//略...
JNIEXPORT void JNICALL Java_Demo_StaticMethod
  (JNIEnv *, jclass, jstring);
//...
JNIEXPORT void JNICALL Java_Demo_InstaceMethod (JNIEnv *, jobject); //略...

 一些简单的例子都不会用到jclass,但是一旦有用到静态的方法去操作一些东西,就会用到jclass这个类引用了。他们唯独的区别其实也就在这了。

  1:JNI的传参

  在上一篇博文里已经提到了JAVA层的对象传递给了JNI层。它是通过一个C指针类型,指向了JAVA VM的内部结构,因此千万不可以为了效率或者方便起见而绕过JNI的内部函数直接操作这个指针。同时JNI中的前面两个参数也在上篇文章已经介绍过,如果涉及到在native方法内传递参数,那么就涉及到第三个参数了。比如需要传递一个字符串,那么第三个参数就会是jstring。这里的jstring是一种类型。在JNI中,数据类型基本上可以这么认为,在原有的大家所熟悉的数据类型前面加了j。比如int类型为jint、boolean为jboolean。对应的方法也一应俱全,相信老前辈们已经将它完善的很好了。况且如今IDE这么强大(我目前都是用sublime写java,命令行编译,更别说别的IDE了),再加上网络。用多了自然就熟悉啦。下面举个小例子介绍一下。

    1.1字符串

先看java代码:

//Prompt.java
class Prompt { static { System.loadLibrary("Prompt");//提示加载名字为Prompt.dll库 } private native String getLine(String prompt);//声明native方法 public static void main(String argv[]) { Prompt p = new Prompt(); String input = p.getLine("Type a line:");//字符串作为参数 System.out.println("User typed:" + input); } }

   

//Prompt.c
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <jni.h> #include "Prompt.h" JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) { char buf[128]; const jbyte *str; //将字符串转换成UTF-8 str = (*env)->GetStringUTFChars(env, prompt, NULL); if (str == NULL) { return NULL; } printf("%s", str); //释放转换时候的内存 (*env)->ReleaseStringUTFChars(env, prompt, str); scanf("%s", buf); //构建一个新的字符串返回 return (*env)->NewStringUTF(env, buf); }

   如果直接打印printf("%s", prompt);jstring和char*是不同类型。需要转换成C能识别的char*,JNI支持Unicode/UTF-8字符编码互转的。这里的 几个方法要记住,字符串操作很普遍哦。这里看记得调用ReleaseStringUTFChars释放GetStringUTFChars中分配的内存。其中“str = (*env)->GetStringUTFChars(env, prompt, NULL);”中第三个参数原型是jboolean *。为JNI_TRUE会返回一个拷贝,不关心的时一般设置为NULL。因此这里不确定是否会分配内存。由于GC(垃圾回收机制)的原因,这里可以理解是回收内存,或者回收了一个内存占用标记。如果是后者,那么string的内存呢暂时未被及时回收,并且又有大量的操作,这样就很有可能导致程序崩溃,这是不安全的,所以要及时释放内存。这里也体现了一点,JNI让JAVA丢失了一部分安全性,需要程序员自己去考虑了。

    1.2数组

    这里代码就不贴了,上一篇文章中有一段关于二维数组的代码。

    数组是由基本数据类型构成的集合,先调用FindClass获得一个一维某类型数组。可以调用NewObjectArray分配一个对象数组。也是Stirng的操作方式。

    数组类型就不再是基本数据类型的传递了,它大致和下面要说的类成员和方法的访问方式类似。这里不多说了。

 

  2:类的成员和方法的访问

    2.1成员的访问方式

      有静态成员和实例成员两种访问方式。其实过程都一样,这里贴出静态成员访问方法,实例成员只要把实现方法中的static去掉就可以了,其实这都是VS会自动提示你的。只管放心大胆的用。

//访问类的静态成员
JNIEXPORT void JNICALL Java_StaticField_accessField
(JNIEnv *env, jobject obj)
{
	jclass jcls;
	jint num;
	jfieldID jfid;

	//得到jclass
	jcls = (*env)->GetObjectClass(env, obj);
      //GetObjectClass和FindClass一样,都能得到jclass。前者需要jni传入的一个引用,后者需要完整类名 if (NULL == jcls) { return; } //得到jfield jfid = (*env)->GetStaticFieldID(env, jcls, "num", "I");//和取得实例成员ID的不同的是加了static if (NULL == jfid) { return; } //得到成员值 num = (*env)->GetStaticIntField(env, jcls, jfid); printf("in C:\n"); printf("StaticField.num = %d\n", num); //构建并修改值 (*env)->SetStaticIntField(env, jcls, jfid, 9999); }

 

    2.2类方法的访问方式

  

JNIEXPORT void JNICALL Java_StaticMethod_StaticMethod
(JNIEnv *env, jobject obj)
{
	jclass jcls;
	jmethodID jmid;
	
	jcls = (*env)->GetObjectClass(env, obj);
	if (NULL == jcls)
	{
		return;
	}

	jmid = (*env)->GetStaticMethodID(env,jcls,"callback","()V");
	if (NULL == jmid)
	{
		return;
	}

	//改进方法
	static jmethodID jmid_s;
	if (NULL == jmid_s)
	{
		jmid_s = (*env)->GetStaticMethodID(env, jcls, "callback", "()V");
		if (NULL == jmid_s)
		{
			return;
		}
	}
		//再将下边的jmid改成jmid_s,这样便可以实现加载一次之后,知道被卸载都不再对名称和符号表进行查询操作
		//可以将初始化的field和method一起缓存。这样再次装载就会更新值。

		//改进二:可以首次加载的时候将一个类的所有field和method的信息加载进Hash表。
		//以后的操作只要最这个表进行操作就OK了。用名字和类型做键值

	printf("In C call the Java instance method:\n-------------\n");
	(*env)->CallStaticVoidMethod(env, jcls, jmid);
}

    2.3 访问优化方式

     见变代码中的注释。同样可以类推到其他方式总

  3:关于全局引用和局部引用

  JNI支持3种引用,全局引用,局部引用,弱全局引用。

  全局引用:和全部变量差不多,和程序共存亡,必须通过NewGlobalRef由程序员主动创建。

  局部引用:每创建一个实例就会返回一个指向这个实例的局部引用,只在本线程中native方法中有效。当native 返回时会自动释放。

  弱全局引用 特性与GlobalRef相同,但该类型的引用不保证不被自动回收

 

【再探JNI__2】

标签:

原文地址:http://www.cnblogs.com/ZrBlog/p/4392438.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!