Anroid NDK初体验(下)

作者: sunnygarden | 来源:发表于2016-10-14 19:07 被阅读231次

    在上一篇的Android NDK初体验(上)中讲的是NDK最基础的用法,从工程的建立到最后成功调用native方法的过程。这是最基本的NDK开发,不熟悉NDK具体开发流程的可以先去看看我的上一篇博客。然而仅仅使java层能够调用native层的代码是远远不够的,下面说一些常用的基础扩展知识。

    一、在native代码中添加log

    我们知道,在Android的开发过程中提供的日志工具logcat是非常有用的,能提供五种控制级别帮助我们有效地过滤信息,排查错误。那么,在NDK开发中我们能否引用类似Android系统的Log工具呢?幸运的是,NDK给我们提供了这样的渠道。若要在我们的C代码中引入log,只需要在.c文件中引入如下头文件就能像Android一样使用log啦:

    #include <android/log.h>
    

    我们可以按住ctrl打开这里引用的log.h文件,可以看到在这个头文件里有几个函数,其中__android_log_print()函数就是我们要用到的log打印函数。这个函数有几个参数,其中prio就是我们Andorid里面的Log的级别,可以看到上面定义的android_LogPriority里有我们Android对应的五种控制类型。

    另外需要注意,引入log需要添加一些配置语句:

    • 在eclipse下使用log需要在工程的jni目录下的Android.mk文件中添加:
    LOCAL_LDLIBS += -llog
    
    • 在Android Studio下使用log需在工程的app模块下的build.gradle文件中的ndk模块引入:ldLibs "log",否则会出现Error:undefined reference to `__android_log_print'
    ndk {   
             moduleName "hello" //定义NDKlibrary的名字    
             ldLibs "log" //引入log
        }
    

    我们知道eclipse下的logcat信息是有颜色区别的,我们能一眼看出这是提示信息还是警告信息,但是Android Studio中默认的日志颜色只有红色和白色两种,非常的不直观。这里告诉大家一个Android Studio上配置logcat输出不同控制级别日志颜色的小技巧。

    接下来我们就可以在我们的C文件中添加相应的log打印语句了:

    #include <stdio.h>
    #include <stdlib.h>
    #include <jni.h>
    #include <android/log.h>
    #define TAG "myTag" //自定义Tag
    #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,TAG,__VA_ARGS__) //自定义打印函数LOGV
    JNIEXPORT jstring JNICALLJava_com_test_ndk_hellondk_MainActivity_getHelloStringFromC(JNIEnv *env, jclass type) 
    {    
         LOGV("log from native!");   //调用log打印函数
         return (*env)->NewStringUTF(env,"Get Hello String from C!");
    }
    

    可以看出我们的自定义日志myTag打印在logcat栏,且打印级别为我们设置的VERBOSE(V)级别。


    到这里日志打印功能已经成功添加进去了。

    二、 java层和native层交互###

    上一篇文章讲的是ndk的最基础的用法,调用native方法获得native层传来的字符串并显示出来,只涉及到java层调用native层,那么native层能不能用java层的数据呢?答案是肯定的,这里就要涉及到java层和native层的交互。
      java层与native层的基本数据类型表示有所不同,对应关系如下表:

    从对应关系表中可以看出,native的基本数据类型只需要在java的基本数据类型前加上字母“j”即可。需要注意的是java中的void类型在native中仍是void类型。了解了类型之间的基本转换关系,下面举例说明几种函数的简单扩展用法:

    例:在java层调用native层函数,将数组作为参数传入,要求native层修改数组的内容并返回给java层。

    要实现这个函数,首先我们需要在MainActivity.java中写native方法:

    static{
            System.loadLibrary("hello");
        }
    public static native int[] updateIntArray(int[] data);//将待修改数组作为参数传给native层
    

    写好以后指定updateIntArray()函数使用Alt+Enter,在jni目录下hello.c文件中自动生成对应函数,向函数体内部添加代码即可。

    JNIEXPORT jintArray
    JNICALLJava_com_test_ndk_hellondk_MainActivity_updateIntArray(JNIEnv *env, jclass type, jintArray data_)
     {   
     jint *data = (*env)->GetIntArrayElements(env, data_, NULL); 
    
       // TODO   
    
     (*env)->ReleaseIntArrayElements(env, data_, data, 0);
    }
    

    这里我们有两种方法来改变传入的int数组的值:
    (1)方法一:native层获取java层传递的数组,修改其数据后返回该数组

    JNIEXPORT jintArray
    JNICALLJava_com_test_ndk_hellondk_MainActivity_updateIntArray(JNIEnv *env, jclass type, jintArray array)
     {  
          jint nativeArray[5];     
         (*env)->GetIntArrayRegion(env,array,0,5,nativeArray);   //生成native层的数组拷贝
          int j;     
          for(j = 0; j<5;j++)
           {         
          nativeArray[j]+=5; //native修改数组内容
          LOGV("from c int %d",nativeArray[j]);     
           }     
         (*env)->SetIntArrayRegion(env,array,0,5,nativeArray);  //设置新的array数组的值   
         return array;
    }
    

    其中GetIntArrayRegion()函数和SetIntArrayRegion()函数在我们引入的jni.h文件里有相关的函数声明,而关于具体函数的定义可以自行上网查阅使用。
    (2)方法二:获得指向数组元素的指针,对指针所指向数组内容进行修改

    JNIEXPORT jintArray
    JNICALLJava_com_test_ndk_hellondk_MainActivity_updateIntArray(JNIEnv *env, jclass type, jintArray array) 
    {
          jint* data =(*env)->GetIntArrayElements(env,array,NULL); //返回jint*
          jsize len = (*env)->GetArrayLength(env,array); //返回int数组长度
          int j;
          for(j = 0;j<len;j++)
          {    
             data[j]+=3;    
             LOGV("from c int %d",data[j]);
          }
          (*env)->ReleaseIntArrayElements(env,array,data,0);//释放相关的资源
          return array;
    }
    

    在MainActivity.java中写上相应的调用函数并打印相关log,运行一下,可以看见log的显示表示native层接收到java层的数据并成功修改返回给java层。


      Demo很简单,中间要是有任何问题可以检查环境是否配对,代码是否写对,可以参考我的源码工程,已经上传到github上,感兴趣的可以去我的github上下载,下载地址:NDK简单Demo

    相关文章

      网友评论

      • 阿群1986:需要通过NDK加载的模块分类
        图像处理强化:jpeg-turbo libpng
        特定网络通讯协议支持:例如libvnc
        其他第三方工具SDK:地图/网盘同步/网银支付/游戏引擎

      本文标题:Anroid NDK初体验(下)

      本文链接:https://www.haomeiwen.com/subject/pgmiyttx.html