zoukankan      html  css  js  c++  java
  • 【再探JNI__2】

    昨天,我写了一下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: "); printf("StaticField.num = %d ", 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:
    -------------
    ");
    	(*env)->CallStaticVoidMethod(env, jcls, jmid);
    }
    

        2.3 访问优化方式

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

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

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

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

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

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

  • 相关阅读:
    How to create jar for Android Library Project
    Very large tabs in eclipse panes on Ubuntu
    64bit Ubuntu, Android AAPT, R.java
    Linux(Ubuntu)下如何安装JDK
    Configure xterm Fonts and Colors for Your Eyeball
    建立、配置和使用Activity——启动其他Activity并返回结果
    建立、配置和使用Activity——使用Bundle在Activity之间交换数据
    建立、配置和使用Activity——启动、关闭Activity
    建立、配置和使用Activity——Activity
    异步任务(AsyncTask)
  • 原文地址:https://www.cnblogs.com/ZrBlog/p/4392438.html
Copyright © 2011-2022 走看看