Android Studio下JNI编程2(接上篇,详细讲解java与c层互传参数)

来源:互联网 发布:网络测试仪怎么用 编辑:程序博客网 时间:2024/05/16 11:29

转自:http://blog.csdn.net/huangximin1990/article/details/50830797

Android Studio下JNI环境搭建、编译、运行等可以参考:

http://blog.csdn.net/huangximin1990/article/details/50441400


上篇文章讲述的JNI示例程序主要涉及两个文件:

NativeKit.java

cn_com_losy_jnitest_jni_NativeKit.cpp


注:由于字体调整关系,下列所示代码多了很多<span></span>标签。需要源码的同学可直接到git下载

https://git.oschina.net/huangximin/JniTest-AS.git


NativeKit.java文件(各方法功能注释写得很清楚,不做赘述)

[java] view plain copy
  1. /** 
  2.  * Created by huangximin on 2015/12/24. 
  3.  * 说明:本类中以Java层为基本视角 
  4.  *       Java层向native层传参称为传入 
  5.  *       native层向Java层传参称为传出 
  6.  */  
  7. public class NativeKit {  
  8.     private String name = "Java";  
  9.   
  10.     // 回调方法.native层的doCallback方法会调用本方法  
  11.     public void callbackForJni(String fromNative) {  
  12.         Log.d("jni""jni string from native:" + fromNative);  
  13.     }  
  14.   
  15.     // 加载so文件  
  16.     static {  
  17.         System.loadLibrary("JniDemo");  
  18.     }  
  19.   
  20.     // 求平方.分别测试int/float/double/long的传入和传出  
  21.     public native int square(int num);  
  22.     public native float square(float num);  
  23.     public native double square(double num);  
  24.     public native long square(long num);  
  25.     // 欢迎词.测试String的传入和传出  
  26.     public native String greetings(String username);  
  27.     // 获取二维数组.dimon*dimon的int型数组  
  28.     public native int[][] getTwoArray(int dimon);  
  29.     // 设置name.测试native层直接修改Java类成员变量  
  30.     public native void nativeSetName();  
  31.     // 回调.这个方法有点绕.native层的doCallback方法会再调用Java层(本类)的callbackForJni方法  
  32.     public native void doCallback();  
  33.     // 获取User实例.测试native层创建对象,然后返回给Java层  
  34.     public native User nativeGetUser();  
  35.     // native层获取Java层对象,并获取成员变量  
  36.     public native void printUserInfoAtNative(User user);  
  37.     // 修改User对象成员变量.同时测试Java层对象的传入、修改、传出  
  38.     public native User changeUserInfo(User user);  
  39.     // 获取对象列表.测试native层创建Java层对象列表,并返回  
  40.     public native ArrayList<User> nativeGetUserList(int num);  
  41.   
  42.     public String getName() {  
  43.         return name;  
  44.     }  
  45.   
  46.     public void setName(String name) {  
  47.         this.name = name;  
  48.     }  
  49. }  


cn_com_losy_jnitest_jni_NativeKit.cpp文件的内容比较多,按照NativeKit类中方法声明顺序逐个看。

[java] view plain copy
  1. <span style="font-size:18px;color:#ff0000;">public native int square(int num);</span>  
方法对应以下述方法:
[java] view plain copy
  1. <span style="font-size:18px;">JNIEXPORT jint JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_square__I  
  2.         (JNIEnv * env, jobject obj, jint num) {  
  3.     return num * num;  
  4. }</span>  
其中,

JNIEXPORT 和JNICALL为JNI关键字,不用管它们

jint   对应int (具体数据类型对应详见本文最后)

[java] view plain copy
  1. Java_cn_com_losy_jnitest_jni_NativeKit_square__I 指明对应的方法  

cn.com.losy.jnitest.jni包中的NativeKit类中的square方法

注:由于该类中square方法存在重载现象,而C语言没有重载,JNI的处理方法是在上述方法名最后面增加了

[java] view plain copy
  1. __I  (后面的I表示参数为一个int  
字样以区分不同的方法

JNIEnv * env   JNIEnv 是JNI核心,有众多用处,后面会看到

jobject  obj    这个jobject需要两种情况分析。上段代码中square方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是obj就是一个NativeKit实例。如果square是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用NativeKit.square()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是obj就是NativeKit.class。

jint num  即NativeKit中square(int num)方法传下来的参数

---------------------------------------

[java] view plain copy
  1. public native float square(float num);  
  2. public native double square(double num);  
  3. public native long square(long num);  
这三个方法与第一个方法类似,不做赘述

----------------------------------------

[java] view plain copy
  1. public native String greetings(String username);  
对应

[java] view plain copy
  1. JNIEXPORT jstring JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_greetings  
  2.         (JNIEnv * env, jobject obj, jstring jstr) {  
  3.     // 读取来自Java层的string  
  4.     const char* str = <span style="color:#33cc00;">env->GetStringUTFChars(jstr, false);</span>  
  5.     if(str == NULL) {  
  6.         return NULL; /* OutOfMemoryError already thrown */  
  7.     }  
  8.     // 显示string  
  9.     std::printf("%s", str);  
  10.     std::printf("\n");  
  11.     // 释放资源  
  12.     <span style="color:#33cc00;">env->ReleaseStringUTFChars(jstr, str);</span>  
  13.     // 返回新string  
  14.     const char* tmpstr = "I am from Jni";  
  15.     <span style="color:#33cc00;">jstring rtstr = env->NewStringUTF(tmpstr);</span>  
  16.     return rtstr;  
  17. }  
其中,值得注意的是读取String需

[java] view plain copy
  1. const char* str = env->GetStringUTFChars(jstr, false);  
构造String需

[java] view plain copy
  1. const char* tmpstr = "I am from Jni";  
  2. jstring rtstr = env->NewStringUTF(tmpstr);  
---------------------------------------

[java] view plain copy
  1. public native int[][] getTwoArray(int dimon);  
对应

[java] view plain copy
  1. JNIEXPORT jobjectArray JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_getTwoArray  
  2.         (JNIEnv * env, jobject obj, jint dimion) {  
  3.     if (dimion > 10) { // 假设 dimion  <= 10 ;  
  4.         return NULL;  
  5.     }  
  6.     // 获得一维数组 的类引用,即jintArray类型  
  7.     jclass intArrayClass = <span style="color:#009900;">env->FindClass("[I");</span>  
  8.     // 构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion  
  9.     jobjectArray obejctIntArray = <span style="color:#009900;">env->NewObjectArray(dimion, intArrayClass, NULL);</span>  
  10.   
  11.     // 构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组  
  12.     forint i = 0; i< dimion; i++ ) {  
  13.         // 构建jint型一维数组  
  14.         jintArray intArray = <span style="color:#33cc00;">env->NewIntArray(dimion);</span>  
  15.         // 设置jint型一维数组的值  
  16.         jint temp[10];  // 初始化一个容器,假设 dimion  <= 10 ;  
  17.         forint j = 0; j < dimion; j++) {  
  18.             temp[j] = i + j; //赋值  
  19.         }  
  20.         // 将temp数组复制到intArray  
  21.         <span style="color:#33cc00;">env->SetIntArrayRegion(intArray, 0, dimion, temp);</span>  
  22.         // 给object对象数组赋值,即保持对jint一维数组的引用  
  23.         <span style="color:#33cc00;">env->SetObjectArrayElement(obejctIntArray, i, intArray);</span>  
  24.         // 删除局部引用  
  25.         <span style="color:#33cc00;">env->DeleteLocalRef(intArray);</span>  
  26.     }  
  27.   
  28.     return  obejctIntArray; // 返回该对象数组  
  29. }  
-------------------------------------

[java] view plain copy
  1. public native void nativeSetName();  
对应

[java] view plain copy
  1. JNIEXPORT void JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeSetName  
  2.         (JNIEnv * env, jobject obj) {  
  3.     jclass cls = <span style="color:#33cc00;">env->GetObjectClass(obj);</span> //获得Java层该对象实例的类引用,即NativeKit类引用  
  4.   
  5.     jfieldID nameFieldId = <span style="color:#33cc00;">env->GetFieldID(cls, "name""Ljava/lang/String;");</span> //获得属性句柄  
  6.   
  7.     if(nameFieldId == NULL) {  
  8.         std::printf(" 没有得到name 的句柄Id \n;");  
  9.     }  
  10.     jstring javaNameStr = <span style="color:#33cc00;">(jstring) env->GetObjectField(obj ,nameFieldId);</span> // 获得该属性的值  
  11.     const char * c_javaName = <span style="color:#33cc00;">env->GetStringUTFChars(javaNameStr , NULL);</span> //转换为 char *类型  
  12.   
  13.     std::printf("%s", c_javaName); //输出显示  
  14.     std::printf("\n");  
  15.   
  16.     <span style="color:#33cc00;">env->ReleaseStringUTFChars(javaNameStr , c_javaName);</span>  //释放局部引用  
  17.   
  18.     // 构造一个jString对象  
  19.     const char * c_ptr_name = "setByNative";  
  20.     jstring cName = <span style="color:#33cc00;">env->NewStringUTF(c_ptr_name);</span>  
  21.   
  22.     <span style="color:#33cc00;">env->SetObjectField(obj, nameFieldId, cName);</span> // 设置该字段的值  
  23. }  
-------------------------------------------

[java] view plain copy
  1. public native void doCallback();  
对应

[java] view plain copy
  1. JNIEXPORT void JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_doCallback  
  2.         (JNIEnv * env, jobject obj) {  
  3.     jclass cls = <span style="color:#33cc00;">env->GetObjectClass(obj);</span> // 获得NativeKit类引用  
  4.     jmethodID callbackMethodID = <span style="color:#33cc00;">env->GetMethodID(cls, "callbackForJni""(Ljava/lang/String;)V");</span> // 获得该回调方法句柄  
  5.   
  6.     if(callbackMethodID == NULL) {  
  7.         std::printf("doCallback getMethodId is failed \n");  
  8.     }  
  9.   
  10.     jstring native_desc = <span style="color:#33cc00;">env->NewStringUTF("callback From Native");</span>  
  11.   
  12.     // 回调该方法,并且传递参数值  
  13.     <span style="color:#33cc00;">env->CallVoidMethod(obj, callbackMethodID, native_desc);</span>  
  14. }  
-------------------------------------------

[java] view plain copy
  1. public native User nativeGetUser();  
对应

[java] view plain copy
  1. JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeGetUser  
  2.         (JNIEnv * env, jobject obj) {  
  3.     jclass usercls = <span style="color:#33cc00;">env->FindClass("cn/com/losy/jnitest/jni/User");</span> // 获取User对象引用  
  4.   
  5.     //获得得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V  
  6.     jmethodID constructMID = <span style="color:#33cc00;">env->GetMethodID(usercls,"<init>","(ILjava/lang/String;)V");</span>  
  7.   
  8.     jstring name = <span style="color:#33cc00;">env->NewStringUTF("HXM from Native");</span>  
  9.   
  10.     // 构造一个对象,调用该类的构造函数,并且传递参数  
  11.     jobject userojb = <span style="color:#33cc00;">env->NewObject(usercls, constructMID, 11, name);</span>  
  12.   
  13.     return userojb;  
  14. }  
--------------------------------------
[java] view plain copy
  1. public native void printUserInfoAtNative(User user);  

相关的点包含在下一个方法

--------------------------------------

[java] view plain copy
  1. public native User changeUserInfo(User user);  
对应

[java] view plain copy
  1. JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_changeUserInfo  
  2.         (JNIEnv * env, jobject obj, jobject userobj) {  
  3.     jclass usercls = <span style="color:#33cc00;">env->GetObjectClass(userobj);</span> // 或得User类引用  
  4.   
  5.     if(usercls == NULL) {  
  6.         std::printf("GetObjectClass failed \n");  
  7.     }  
  8.   
  9.     jfieldID ageFieldID = <span style="color:#33cc00;">env->GetFieldID(usercls,"age","I");</span> // 获得得User类的属性id  
  10.     jfieldID nameFieldID = <span style="color:#33cc00;">env->GetFieldID(usercls,"name","Ljava/lang/String;");</span> // 获得属性ID  
  11.   
  12.     jint age = <span style="color:#33cc00;">env->GetIntField(userobj, ageFieldID);</span>  // 获得属性值  
  13.     jstring name = <span style="color:#33cc00;">(jstring)env->GetObjectField(userobj, nameFieldID);</span>// 获得属性值  
  14.   
  15.     const char * c_name = <span style="color:#33cc00;">env->GetStringUTFChars(name, false);</span>// 转换成 char *  
  16.   
  17.     // 显示user信息  
  18.     std::printf("show user info age:%d name:%s", age, c_name);  
  19.   
  20.    <span style="color:#33cc00;"> env->ReleaseStringUTFChars(name, c_name); </span>//释放引用  
  21.   
  22.     // 构造一个jString对象  
  23.     const char * c_ptr_name = "quyuan";  
  24.     jstring cName = <span style="color:#33cc00;">env->NewStringUTF(c_ptr_name);</span>  
  25.     <span style="color:#33cc00;">env->SetObjectField(userobj, nameFieldID, cName);</span> // 设置该字段的值  
  26.   
  27.     <span style="color:#33cc00;">env->SetIntField(userobj, ageFieldID, 55);</span> // 设置该字段的值  
  28.   
  29.     return userobj;  
  30. }  
-------------------------------------------

[java] view plain copy
  1. public native ArrayList<User> nativeGetUserList(int num);  
对应

[java] view plain copy
  1. JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeGetUserList  
  2.         (JNIEnv * env, jobject obj, jint size) {  
  3.     jclass listcls = <span style="color:#33cc00;">env->FindClass("java/util/ArrayList");</span>// 获得ArrayList类引用  
  4.   
  5.     if(listcls == NULL) {  
  6.         std::printf("listcls is null \n");  
  7.     }  
  8.     jmethodID list_construct = <span style="color:#33cc00;">env->GetMethodID(listcls , "<init>","()V");</span>// 获得得构造函数Id  
  9.     jobject listobj = <span style="color:#33cc00;">env->NewObject(listcls, list_construct);</span> // 创建一个Arraylist集合对象  
  10.     // 获得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;  
  11.     jmethodID list_add  = <span style="color:#33cc00;">env->GetMethodID(listcls,"add","(Ljava/lang/Object;)Z");</span>  
  12.     jclass usercls = <span style="color:#33cc00;">env->FindClass("cn/com/losy/jnitest/jni/User"); </span>// 获得User类引用  
  13.     //获得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V  
  14.     jmethodID user_costruct = <span style="color:#33cc00;">env->GetMethodID(usercls , "<init>""(ILjava/lang/String;)V");</span>  
  15.   
  16.     for(int i = 0; i < size; i++) {  
  17.         jstring str = <span style="color:#33cc00;">env->NewStringUTF("Native");</span>  
  18.         //通过调用该对象的构造函数来new 一个 Student实例  
  19.         jobject stu_obj = <span style="color:#33cc00;">env->NewObject(usercls , user_costruct, 10, str);</span>  //构造一个对象  
  20.   
  21.         <span style="color:#33cc00;">env->CallBooleanMethod(listobj, list_add, stu_obj);</span> // 执行Arraylist类实例的add方法,添加一个stu对象  
  22.     }  
  23.   
  24.     return listobj ;  
  25. }  
------------------------------------

数据类型对应关系如下表:(来自百度百科)
Java 类型本地 C 类型
实际表示的 C 类型
(Win32)
说明booleanjbooleanunsigned char无符号,8 位bytejbytesigned char有符号,8 位charjcharunsigned short无符号,16 位shortjshortshort有符号,16 位intjintlong有符号,32 位longjlong__int64有符号,64 位floatjfloatfloat32 位doublejdoubledouble64 位voidvoidN/AN/AJava的String   转换为c的char*
原创粉丝点击