Andorid Studio NDK开发-使用库

agvo4493 4年前
   <p>C语言是一个巨大的宝库,系统底层的很多的实现都是基于C语言实现的,比如图像处理,加密等。</p>    <p>C语言的运行效率也是很高的,因此为了效率有时候也会引入第三方的C语言库。</p>    <p>总而言之,会在NDK开发的过程中会使用大量的库,系统自带的库,第三方库等。</p>    <p>在 gradle-experimental 中使用C语言的库是非常便利的。</p>    <h2>调用系统库</h2>    <p>Log是在Android开发过程用来调试程序必备的工具之一,如何在NDK中使用 android.util.Log 方便在Logcat中查看JNI程序的运行情况呢?这就需要在NDK中导入Android系统的 Log 库。</p>    <p>首先需要在在 gradle 中引入Log库:</p>    <pre>  model{   ....   android {          compileSdkVersion 23          buildToolsVersion "23.0.2"          ndk {              moduleName "experiment"                  ldLibs.addAll([ 'log']);          }     }   }</pre>    <p>直接在ldLIbs中加入log就可以,如果需要引入其他的系统库,只要在数组中直接增加即可。再定义一个native的方法:</p>    <p>public static native void callLogFromJni();</p>    <p>在Jni中调用Log库的方法:</p>    <pre>  //引入 log  #include <android/log.h>    JNIEXPORT void JNICALL  Java_com_jjz_NativeUtil_callLogFromJni(JNIEnv *env, jclass type) {        __android_log_print(ANDROID_LOG_INFO,"jni-log","from jni log");    }</pre>    <p>第一个参数, ANDROID_LOG_INFO 是log的级别他包含:</p>    <pre>  typedef enum android_LogPriority {      ANDROID_LOG_UNKNOWN = 0,      ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */      ANDROID_LOG_VERBOSE,      ANDROID_LOG_DEBUG,      ANDROID_LOG_INFO,      ANDROID_LOG_WARN,      ANDROID_LOG_ERROR,      ANDROID_LOG_FATAL,      ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */  } android_LogPriority;  </pre>    <p>一般我们常用的是 ADNROID_LOG_VERBOSE , ANDROID_LOG_DEBUG , ANDROID_LOG_INFO , ANDROID_LOG_WARN , ANDROID_LOG_ERROR 分别对应java中的 Log.v , Log.d , Log.i , Log.w , Log.e 。</p>    <p>第二个参数是tag,用来方便的对log分类。</p>    <p>第三个参数是message,对应log的具体信息。</p>    <p>一般还会采用宏定义的方式,定义Log的输出的方法,方便调用:</p>    <pre>  #define LOG_TAG "jni-log"  #define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)  </pre>    <p>这里定义了一个warning log的宏,在代码里面可以直接调用:</p>    <pre>    LOGW("log from  define");</pre>    <h2>使用第三方类库</h2>    <p>OpenSSL是最常用的加密库之一,下面以OpenSSL为例,介绍下在 gradle-experimental 中如何引入第三方类库。关于如何编译Android下的OpenSSL详见: <a href="/misc/goto?guid=4959674734260670516" rel="nofollow,noindex">编译Android的OpenSSL类库</a> 。</p>    <p>首先定义对于库的 repositories :</p>    <pre>  model {     repositories{          libs(PrebuiltLibraries) {              // Configure one pre-built lib: static              openssl {                  // 头文件地址                  headers.srcDir "/usr/local/ssl/android-23/include"                  // 静态链接库的引用,                  binaries.withType(StaticLibraryBinary) {                      staticLibraryFile = file("libs/libcrypto.a")                  }                  //动态链接库的引用  //                binaries.withType(SharedLibraryBinary) {  //                    sharedLibraryFile = file("libs/libcrypto.so")  //                }              }            }        }    }</pre>    <p>c语言的类库分为静态链接库(.a)和动态链接库(.so),静态类库和动态类库的引入方式是不一样的,分为对应: StaticLibraryBinary 和 SharedLibraryBinary 。这里引入的库为静态链接库,名称为: openssl .</p>    <p>指定库依赖:</p>    <pre>  model{   ......   android{    .....     sources {              main {                  jni {                      dependencies{                          library 'openssl' linkage 'static'                          //动态链接库                          //  library 'openssl' linkage 'shared'                      }                      source {                          srcDir "src/main/jni"                      }                  }                  jniLibs{                      source{                          srcDir "libs/"                      }                  }                  java{                      source{                          srcDir "src/main/java"                      }                  }              }          }   }  }</pre>    <p>这里在model.android.sources.main中指定库的依赖为上面定义的 openssl , linkage 类型为static,如果是动态链接库 linkage 就是shared。</p>    <p>因为编译的OpenSSL只支持arm结构的cpu,因此需要指定abi为对应的cpu,在model.android添加配置:</p>    <pre>   ndk {        moduleName "experiment"        abiFilters.addAll(['armeabi', 'armeabi-v7a'])             }</pre>    <h2>使用OpenSSL</h2>    <p>首先定义一个 native 方法,需要从OpenSSL中读取随机数:</p>    <pre>  public static native byte[] getRandom();</pre>    <p>生成对应的JNI方法:</p>    <pre>  //引入OpenSSL的rand  #include <openssl/rand.h>  JNIEXPORT jbyteArray JNICALL  Java_com_jjz_NativeUtil_getRandom(JNIEnv *env, jclass type) {      unsigned char rand_str[128];      RAND_seed(rand_str, 32);      jbyteArray bytes = (*env)->NewByteArray(env, 128);      (*env)->SetByteArrayRegion(env, bytes, 0, 128, rand_str);      return bytes;    }</pre>    <p>RAND_seed 是OpenSSL的方法,读取随机数。这段代码就是读取一个128的随机数,然后转换为java的byte[]。</p>    <p>在界面上面使用读取随机数的方法:</p>    <pre>  tv2.setText(Base64.encodeToString(NativeUtil.getRandom(), Base64.DEFAULT));</pre>    <p>运行之后可以在界面看到一段随机的字符串显示:</p>    <p><img src="https://simg.open-open.com/show/c7bb27852f086dbd4f5c24a9c126d3f8.png"></p>    <p>源代码地址: <a href="/misc/goto?guid=4959674734341001191" rel="nofollow,noindex">https://github.com/jjz/android/tree/master/experimental</a></p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959674734433893817" rel="nofollow">https://segmentfault.com/a/1190000005752063</a></p>    <p> </p>