zoukankan      html  css  js  c++  java
  • JNI学习积累之三 ---- 操作JNI函数以及复杂对象传递

    本文原创,转载请注明出处http://blog.csdn.NET/qinjuning




             在掌握了JNI函数的使用和相关类型的映射后,以及知晓何利用javah工具生成对应的jni函数以及如何生成动态

        链接库 (windos下就是.dll库,Linux就是.so库了,不懂在Window下生成dll动态库的,具体流程可看我的这篇博客:

       Android中JNI的使用之一:Java原生JNI的使用、javah指令的使用以及图解教材)。即可掌握JNI的使用了了。


            总的来说,JNI是不难的。通过前面的学习相信你应该有所了解。今天,我们从几个简单的小例子,来对JNI进行下实战训练。

         可都是些小例子,耐心看咯。

     

            主要操作内容,包括如下几个部分:


                   1、在Native层返回一个字符串

                   2、从Native层返回一个int型二维数组(int a[ ][ ]) 

                   3、从Native层操作Java层的类: 读取/设置类属性

                   4、在Native层操作Java层的类:读取/设置类属性、回调Java方法 

                   5、从Native层返回一个复杂对象(即一个类咯)

                   6、在Java层传递复杂对象至Native层

                   7、从Native层返回Arraylist集合对象


          广而告知,这些操作就是简单的利用一些JNI函数即实现了。so easy 。


     一、在Native层返回一个字符串

           Java层原型方法:

    1. public class HelloJni {  
    2.     ...  
    3.     public native void getAJNIString();  
    4.     ...  
    5. }     


           Native层该方法实现为 :

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    getAJNIString 
    4.  * Signature: ()Ljava/lang/String; 
    5.  */   
    6. //返回字符串  
    7. JNIEXPORT jstring JNICALL Java_com_feixun_jni_HelloJni_getAJNIString(JNIEnv * env, jobject obj)  
    8. {  
    9.     jstring str = env->newStringUTF("HelloJNI");  //直接使用该JNI构造一个jstring对象返回  
    10.     return str ;  
    11. }  


     

    二、在Native层返回一个int型二维数组(inta[ ][ ])

        Java层原型方法:

    1. public class HelloJni {  
    2.     ...  
    3.     //参数代表几行几列数组 ,形式如:int a[dimon][dimon]  
    4.     private native int[][] getTwoArray(int dimon) ;   
    5.     ...  
    6. }     


          Native层该方法实现为 :

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    getTwoArray 
    4.  * Signature: (I)[[I 
    5.  */  
    6. //通过构造一个数组的数组, 返回 一个二维数组的形式  
    7. JNIEXPORT jobjectArray JNICALL Java_com_feixun_jni_HelloJni_getTwoArray  
    8.   (JNIEnv * env, jobject object, jint dimion)  
    9. {  
    10.       
    11.     jclass intArrayClass = env->FindClass("[I"); //获得一维数组 的类引用,即jintArray类型  
    12.     //构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion  
    13.     jobjectArray obejctIntArray  =  env->NewObjectArray(dimion ,intArrayClass , NULL);  
    14.   
    15.     //构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组  
    16.     for( int i = 0 ; i< dimion  ; i++ )  
    17.     {  
    18.         //构建jint型一维数组  
    19.         jintArray intArray = env->NewIntArray(dimion);  
    20.   
    21.         jint temp[10]  ;  //初始化一个容器,假设 dimion  < 10 ;  
    22.         for( int j = 0 ; j < dimion ; j++)  
    23.         {  
    24.             temp[j] = i + j  ; //赋值  
    25.         }  
    26.           
    27.         //设置jit型一维数组的值  
    28.         env->SetIntArrayRegion(intArray, 0 , dimion ,temp);  
    29.         //给object对象数组赋值,即保持对jint一维数组的引用  
    30.         env->SetObjectArrayElement(obejctIntArray , i ,intArray);  
    31.   
    32.         env->DeleteLocalRef(intArray);  //删除局部引用  
    33.     }  
    34.   
    35.     return   obejctIntArray; //返回该对象数组  
    36. }  



     三、在Native层操作Java层的类 :读取/设置类属性


         Java层原型方法:

    1. public class HelloJni {  
    2.     ...  
    3.     //在Native层读取/设置属性值  
    4.     public native void native_set_name() ;  
    5.     ...  
    6.       
    7.     private String name = "I am at Java" ; //类属性  
    8. }     


        Native层该方法实现为 :

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    native_set_name 
    4.  * Signature: ()V  
    5.  */  
    6. //在Native层操作Java对象,读取/设置属性等  
    7. JNIEXPORT void JNICALL Java_com_feixun_jni_HelloJni_native_1set_1name  
    8.   (JNIEnv *env , jobject  obj )  //obj代表执行此JNI操作的类实例引用  
    9. {  
    10.    //获得jfieldID 以及 该字段的初始值  
    11.    jfieldID  nameFieldId ;  
    12.   
    13.    jclass cls = env->GetObjectClass(obj);  //获得Java层该对象实例的类引用,即HelloJNI类引用  
    14.   
    15.    nameFieldId = env->GetFieldID(cls , "name" , "Ljava/lang/String;"); //获得属性句柄  
    16.   
    17.    if(nameFieldId == NULL)  
    18.    {  
    19.        cout << " 没有得到name 的句柄Id  ;" ;  
    20.    }  
    21.    jstring javaNameStr = (jstring)env->GetObjectField(obj ,nameFieldId);  // 获得该属性的值  
    22.    const char * c_javaName = env->GetStringUTFChars(javaNameStr , NULL);  //转换为 char *类型  
    23.    string str_name = c_javaName ;    
    24.    cout << "the name from java is " << str_name << endl ; //输出显示  
    25.    env->ReleaseStringUTFChars(javaNameStr , c_javaName);  //释放局部引用  
    26.   
    27.    //构造一个jString对象  
    28.    char * c_ptr_name = "I come from Native" ;  
    29.      
    30.    jstring cName = env->NewStringUTF(c_ptr_name); //构造一个jstring对象  
    31.   
    32.    env->SetObjectField(obj , nameFieldId , cName); // 设置该字段的值  
    33. }  



    四、在Native层操作Java层的类:回调Java方法 

        Java层原型方法:

    1. public class HelloJni {  
    2.     ...  
    3.     //Native层回调的方法实现  
    4.     public void callback(String fromNative){       
    5.         System.out.println(" I was invoked by native method  ############# " + fromNative);  
    6.     };  
    7.     public native void doCallBack(); //Native层会调用callback()方法  
    8.     ...   
    9.       
    10.     // main函数  
    11.     public static void main(String[] args)   
    12.     {  
    13.         new HelloJni().ddoCallBack();  
    14.     }     
    15. }     


        Native层该方法实现为 :

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    doCallBack 
    4.  * Signature: ()V 
    5.  */  
    6. //Native层回调Java类方法  
    7. JNIEXPORT void JNICALL Java_com_feixun_jni_HelloJni_doCallBack  
    8.   (JNIEnv * env , jobject obj)  
    9. {  
    10.      //回调Java中的方法  
    11.   
    12.     jclass cls = env->GetObjectClass(obj);//获得Java类实例  
    13.     jmethodID callbackID = env->GetMethodID(cls , "callback" , "(Ljava/lang/String;)V") ;//或得该回调方法句柄  
    14.   
    15.     if(callbackID == NULL)  
    16.     {  
    17.          cout << "getMethodId is failed  " << endl ;  
    18.     }  
    19.     
    20.     jstring native_desc = env->NewStringUTF(" I am Native");  
    21.   
    22.     env->CallVoidMethod(obj , callbackID , native_desc); //回调该方法,并且传递参数值  
    23. }  


        接下来,我们会操作复杂对象,也就是Java层的类,包括从Native层返回一个类以及传递一个类到Native层去, 这儿我们

    使用的类非常简单,如下:

         Student.java

    1. package com.feixun.jni;  
    2.   
    3. public class Student  
    4. {  
    5.     private int age ;  
    6.     private String name ;  
    7.     //构造函数,什么都不做  
    8.     public Student(){ }  
    9.       
    10.     public Student(int age ,String name){  
    11.         this.age = age ;  
    12.         this.name = name ;  
    13.     }  
    14.       
    15.     public int getAge() {  
    16.         return age;  
    17.     }  
    18.     public void setAge(int age) {  
    19.         this.age = age;  
    20.     }  
    21.     public String getName() {  
    22.         return name;  
    23.     }  
    24.     public void setName(String name){  
    25.         this.name = name;  
    26.     }  
    27.       
    28.     public String toString(){  
    29.         return "name --- >" + name + "  age --->" + age ;  
    30.     }  
    31. }  


     五、在Native层返回一个复杂对象(即一个类咯)


         Java层的方法对应为:

    1. public class HelloJni {  
    2.     ...  
    3.     //在Native层返回一个Student对象  
    4.     public native Student nativeGetStudentInfo() ;  
    5.     ...   
    6. }     


         Native层该方法实现为 :       

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    nativeGetStudentInfo 
    4.  * Signature: ()Lcom/feixun/jni/Student; 
    5.  */  
    6. //返回一个复杂对象  
    7. JNIEXPORT jobject JNICALL Java_com_feixun_jni_HelloJni_nativeGetStudentInfo  
    8.   (JNIEnv * env, jobject obl)  
    9. {  
    10.     //关于包描述符,这儿可以是 com/feixun/jni/Student 或者是 Lcom/feixun/jni/Student;   
    11.     //   这两种类型 都可以获得class引用  
    12.     jclass stucls = env->FindClass("com/feixun/jni/Student"); //或得Student类引用  
    13.   
    14.     //获得得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V  
    15.     jmethodID constrocMID = env->GetMethodID(stucls,"<init>","(ILjava/lang/String;)V");  
    16.   
    17.     jstring str = env->NewStringUTF(" come from Native ");  
    18.   
    19.     jobject stu_ojb = env->NewObject(stucls,constrocMID,11,str);  //构造一个对象,调用该类的构造函数,并且传递参数  
    20.   
    21.   
    22.     return stu_ojb ;  
    23. }  


     六、从Java层传递复杂对象至Native层


         Java层的方法对应为:

    1. public class HelloJni {  
    2.     ...  
    3.     //在Native层打印Student的信息  
    4.     public native void  printStuInfoAtNative(Student stu);  
    5.     ...   
    6. }  


         Native层该方法实现为 :       

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    printStuInfoAtNative 
    4.  * Signature: (Lcom/feixun/jni/Student;)V 
    5.  */  
    6. //在Native层输出Student的信息  
    7. JNIEXPORT void JNICALL Java_com_feixun_jni_HelloJni_printStuInfoAtNative  
    8.   (JNIEnv * env, jobject obj,  jobject obj_stu) //第二个类实例引用代表Student类,即我们传递下来的对象  
    9. {  
    10.       
    11.     jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student类引用  
    12.   
    13.     if(stu_cls == NULL)  
    14.     {  
    15.         cout << "GetObjectClass failed  " ;  
    16.     }  
    17.     //下面这些函数操作,我们都见过的。O(∩_∩)O~  
    18.     jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //获得得Student类的属性id   
    19.     jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 获得属性ID  
    20.   
    21.     jint age = env->GetIntField(objstu , ageFieldID);  //获得属性值  
    22.     jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//获得属性值  
    23.   
    24.     const char * c_name = env->GetStringUTFChars(name ,NULL);//转换成 char *  
    25.    
    26.     string str_name = c_name ;   
    27.     env->ReleaseStringUTFChars(name,c_name); //释放引用  
    28.       
    29.     cout << " at Native age is :" << age << " # name is " << str_name << endl ;   
    30. }  



     七、最后加个难度,即在Native层返回集合对象(留这儿,以后也好找点)


         Java层的对应方法为:

    1. public class HelloJni {  
    2.     ...  
    3.     //在Native层返回ArrayList集合   
    4.     public native ArrayList<Student> native_getListStudents();  
    5.     ...   
    6. }     


         Native层该方法实现为 :       

    1. /* 
    2.  * Class:     com_feixun_jni_HelloJni 
    3.  * Method:    native_getListStudents 
    4.  * Signature: ()Ljava/util/ArrayList; 
    5.  */ //获得集合类型的数组  
    6. JNIEXPORT jobject JNICALL Java_com_feixun_jni_HelloJni_native_getListStudents  
    7.   (JNIEnv * env, jobject obj)  
    8. {  
    9.     jclass list_cls = env->FindClass("Ljava/util/ArrayList;");//获得ArrayList类引用  
    10.   
    11.     if(listcls == NULL)  
    12.     {  
    13.         cout << "listcls is null  " ;  
    14.     }  
    15.     jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //获得得构造函数Id  
    16.   
    17.     jobject list_obj = env->NewObject(list_cls , list_costruct); //创建一个Arraylist集合对象  
    18.     //或得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;  
    19.     jmethodID list_add  = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z");   
    20.     
    21.     jclass stu_cls = env->FindClass("Lcom/feixun/jni/Student;");//获得Student类引用  
    22.     //获得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V  
    23.     jmethodID stu_costruct = env->GetMethodID(stu_cls , "<init>", "(ILjava/lang/String;)V");  
    24.   
    25.     for(int i = 0 ; i < 3 ; i++)  
    26.     {  
    27.         jstring str = env->NewStringUTF("Native");  
    28.         //通过调用该对象的构造函数来new 一个 Student实例  
    29.         jobject stu_obj = env->NewObject(stucls , stu_costruct , 10,str);  //构造一个对象  
    30.           
    31.         env->CallBooleanMethod(list_obj , list_add , stu_obj); //执行Arraylist类实例的add方法,添加一个stu对象  
    32.     }  
    33.   
    34.     return list_obj ;  
    35. }  



             最后,如何调用这些JNI函数,大家都懂的,直接调用即可,我就不在贴代码了,免得罗嗦。



            OK,本次JNI的学习就算告一段落了。下一步该认真仔细学习下Android中JNI的使用了。哎,怎么学的东西又那么多呢? - -

  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6168952.html
Copyright © 2011-2022 走看看