在掌握了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层原型方法:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. publicnativevoidgetAJNIString();
  4. ...
  5. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. *Class:com_feixun_jni_HelloJni
  3. *Method:getAJNIString
  4. *Signature:()Ljava/lang/String;
  5. */
  6. //返回字符串
  7. JNIEXPORTjstringJNICALLJava_com_feixun_jni_HelloJni_getAJNIString(JNIEnv*env,jobjectobj)
  8. {
  9. jstringstr=env->newStringUTF("HelloJNI");//直接使用该JNI构造一个jstring对象返回
  10. returnstr;
  11. }

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

Java层原型方法:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. //参数代表几行几列数组,形式如:inta[dimon][dimon]
  4. privatenativeint[][]getTwoArray(intdimon);
  5. ...
  6. }

Native层该方法实现为 :

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


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


Java层原型方法:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. //在Native层读取/设置属性值
  4. publicnativevoidnative_set_name();
  5. ...
  6. privateStringname="IamatJava";//类属性
  7. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. *Class:com_feixun_jni_HelloJni
  3. *Method:native_set_name
  4. *Signature:()V
  5. */
  6. //在Native层操作Java对象,读取/设置属性等
  7. JNIEXPORTvoidJNICALLJava_com_feixun_jni_HelloJni_native_1set_1name
  8. (JNIEnv*env,jobjectobj)//obj代表执行此JNI操作的类实例引用
  9. {
  10. //获得jfieldID以及该字段的初始值
  11. jfieldIDnameFieldId;
  12. jclasscls=env->GetObjectClass(obj);//获得Java层该对象实例的类引用,即HelloJNI类引用
  13. nameFieldId=env->GetFieldID(cls,"name","Ljava/lang/String;");//获得属性句柄
  14. if(nameFieldId==NULL)
  15. {
  16. cout<<"没有得到name的句柄Id\n;";
  17. }
  18. jstringjavaNameStr=(jstring)env->GetObjectField(obj,nameFieldId);//获得该属性的值
  19. constchar*c_javaName=env->GetStringUTFChars(javaNameStr,NULL);//转换为char*类型
  20. stringstr_name=c_javaName;
  21. cout<<"thenamefromjavais"<<str_name<<endl;//输出显示
  22. env->ReleaseStringUTFChars(javaNameStr,c_javaName);//释放局部引用
  23. //构造一个jString对象
  24. char*c_ptr_name="IcomefromNative";
  25. jstringcName=env->NewStringUTF(c_ptr_name);//构造一个jstring对象
  26. env->SetObjectField(obj,nameFieldId,cName);//设置该字段的值
  27. }


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

Java层原型方法:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. //Native层回调的方法实现
  4. publicvoidcallback(StringfromNative){
  5. System.out.println("Iwasinvokedbynativemethod#############"+fromNative);
  6. };
  7. publicnativevoiddoCallBack();//Native层会调用callback()方法
  8. ...
  9. //main函数
  10. publicstaticvoidmain(String[]args)
  11. {
  12. newHelloJni().ddoCallBack();
  13. }
  14. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. *Class:com_feixun_jni_HelloJni
  3. *Method:doCallBack
  4. *Signature:()V
  5. */
  6. //Native层回调Java类方法
  7. JNIEXPORTvoidJNICALLJava_com_feixun_jni_HelloJni_doCallBack
  8. (JNIEnv*env,jobjectobj)
  9. {
  10. //回调Java中的方法
  11. jclasscls=env->GetObjectClass(obj);//获得Java类实例
  12. jmethodIDcallbackID=env->GetMethodID(cls,"callback","(Ljava/lang/String;)V");//或得该回调方法句柄
  13. if(callbackID==NULL)
  14. {
  15. cout<<"getMethodIdisfailed\n"<<endl;
  16. }
  17. jstringnative_desc=env->NewStringUTF("IamNative");
  18. env->CallVoidMethod(obj,callbackID,native_desc);//回调该方法,并且传递参数值
  19. }


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

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

Student.java

[java] view plain copy print ?
  1. packagecom.feixun.jni;
  2. publicclassStudent
  3. {
  4. privateintage;
  5. privateStringname;
  6. //构造函数,什么都不做
  7. publicStudent(){}
  8. publicStudent(intage,Stringname){
  9. this.age=age;
  10. this.name=name;
  11. }
  12. publicintgetAge(){
  13. returnage;
  14. }
  15. publicvoidsetAge(intage){
  16. this.age=age;
  17. }
  18. publicStringgetName(){
  19. returnname;
  20. }
  21. publicvoidsetName(Stringname){
  22. this.name=name;
  23. }
  24. publicStringtoString(){
  25. return"name--->"+name+"age--->"+age;
  26. }
  27. }

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


Java层的方法对应为:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. //在Native层返回一个Student对象
  4. publicnativeStudentnativeGetStudentInfo();
  5. ...
  6. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. *Class:com_feixun_jni_HelloJni
  3. *Method:nativeGetStudentInfo
  4. *Signature:()Lcom/feixun/jni/Student;
  5. */
  6. //返回一个复杂对象
  7. JNIEXPORTjobjectJNICALLJava_com_feixun_jni_HelloJni_nativeGetStudentInfo
  8. (JNIEnv*env,jobjectobl)
  9. {
  10. //关于包描述符,这儿可以是com/feixun/jni/Student或者是Lcom/feixun/jni/Student;
  11. //这两种类型都可以获得class引用
  12. jclassstucls=env->FindClass("com/feixun/jni/Student");//或得Student类引用
  13. //获得得该类型的构造函数函数名为<init>返回类型必须为void即V
  14. jmethodIDconstrocMID=env->GetMethodID(stucls,"<init>","(ILjava/lang/String;)V");
  15. jstringstr=env->NewStringUTF("comefromNative");
  16. jobjectstu_ojb=env->NewObject(stucls,constrocMID,11,str);//构造一个对象,调用该类的构造函数,并且传递参数
  17. returnstu_ojb;
  18. }


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


Java层的方法对应为:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. //在Native层打印Student的信息
  4. publicnativevoidprintStuInfoAtNative(Studentstu);
  5. ...
  6. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. *Class:com_feixun_jni_HelloJni
  3. *Method:printStuInfoAtNative
  4. *Signature:(Lcom/feixun/jni/Student;)V
  5. */
  6. //在Native层输出Student的信息
  7. JNIEXPORTvoidJNICALLJava_com_feixun_jni_HelloJni_printStuInfoAtNative
  8. (JNIEnv*env,jobjectobj,jobjectobj_stu)//第二个类实例引用代表Student类,即我们传递下来的对象
  9. {
  10. jclassstu_cls=env->GetObjectClass(obj_stu);//或得Student类引用
  11. if(stu_cls==NULL)
  12. {
  13. cout<<"GetObjectClassfailed\n";
  14. }
  15. //下面这些函数操作,我们都见过的。O(∩_∩)O~
  16. jfieldIDageFieldID=env->GetFieldID(stucls,"age","I");//获得得Student类的属性id
  17. jfieldIDnameFieldID=env->GetFieldID(stucls,"name","Ljava/lang/String;");//获得属性ID
  18. jintage=env->GetIntField(objstu,ageFieldID);//获得属性值
  19. jstringname=(jstring)env->GetObjectField(objstu,nameFieldID);//获得属性值
  20. constchar*c_name=env->GetStringUTFChars(name,NULL);//转换成char*
  21. stringstr_name=c_name;
  22. env->ReleaseStringUTFChars(name,c_name);//释放引用
  23. cout<<"atNativeageis:"<<age<<"#nameis"<<str_name<<endl;
  24. }


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


Java层的对应方法为:

[java] view plain copy print ?
  1. publicclassHelloJni{
  2. ...
  3. //在Native层返回ArrayList集合
  4. publicnativeArrayList<Student>native_getListStudents();
  5. ...
  6. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. *Class:com_feixun_jni_HelloJni
  3. *Method:native_getListStudents
  4. *Signature:()Ljava/util/ArrayList;
  5. *///获得集合类型的数组
  6. JNIEXPORTjobjectJNICALLJava_com_feixun_jni_HelloJni_native_getListStudents
  7. (JNIEnv*env,jobjectobj)
  8. {
  9. jclasslist_cls=env->FindClass("Ljava/util/ArrayList;");//获得ArrayList类引用
  10. if(listcls==NULL)
  11. {
  12. cout<<"listclsisnull\n";
  13. }
  14. jmethodIDlist_costruct=env->GetMethodID(list_cls,"<init>","()V");//获得得构造函数Id
  15. jobjectlist_obj=env->NewObject(list_cls,list_costruct);//创建一个Arraylist集合对象
  16. //或得Arraylist类中的add()方法ID,其方法原型为:booleanadd(Objectobject);
  17. jmethodIDlist_add=env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z");
  18. jclassstu_cls=env->FindClass("Lcom/feixun/jni/Student;");//获得Student类引用
  19. //获得该类型的构造函数函数名为<init>返回类型必须为void即V
  20. jmethodIDstu_costruct=env->GetMethodID(stu_cls,"<init>","(ILjava/lang/String;)V");
  21. for(inti=0;i<3;i++)
  22. {
  23. jstringstr=env->NewStringUTF("Native");
  24. //通过调用该对象的构造函数来new一个Student实例
  25. jobjectstu_obj=env->NewObject(stucls,stu_costruct,10,str);//构造一个对象
  26. env->CallBooleanMethod(list_obj,list_add,stu_obj);//执行Arraylist类实例的add方法,添加一个stu对象
  27. }
  28. returnlist_obj;
  29. }



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



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

更多相关文章

  1. Activity 生命周期(一)
  2. Android(安卓)组件系列-----Activity保存状态
  3. Android(安卓)源码解析Handler处理机制(一)
  4. android: 接收和发送短信
  5. Android自学笔记-14-意图(Intent)
  6. Android(安卓)程序在系统中如何完成启动
  7. Activity从屏幕底部滑出、滑入、处理黑色背景和状态栏
  8. Android之AsyncTask的用法
  9. Android之ViewStub的简单使用

随机推荐

  1. Android、iOS 市场份额下降?“奇怪数据”
  2. android中通过代码实现文件权限修改(chmod
  3. Android用户界面设计:线性布局
  4. Android支付宝、微信支付阐述
  5. Android基于Socket无线遥控(2)--模拟触摸按
  6. Android(安卓)ListView 图片异步加载和图
  7. Android、web中的图片和语音的加密
  8. Android(安卓)开发初级入门:注册谷歌地图
  9. Android5.0以上的状态栏透明和沉浸式模式
  10. 关于Android中的Rotate旋转动画应用