Andorid Studio NDK開發-使用庫
C語言是一個巨大的寶庫,系統底層的很多的實現都是基于C語言實現的,比如圖像處理,加密等。
C語言的運行效率也是很高的,因此為了效率有時候也會引入第三方的C語言庫。
總而言之,會在NDK開發的過程中會使用大量的庫,系統自帶的庫,第三方庫等。
在 gradle-experimental 中使用C語言的庫是非常便利的。
調用系統庫
Log是在Android開發過程用來調試程序必備的工具之一,如何在NDK中使用 android.util.Log 方便在Logcat中查看JNI程序的運行情況呢?這就需要在NDK中導入Android系統的 Log 庫。
首先需要在在 gradle 中引入Log庫:
model{ .... android { compileSdkVersion 23 buildToolsVersion "23.0.2" ndk { moduleName "experiment" ldLibs.addAll([ 'log']); } } }
直接在ldLIbs中加入log就可以,如果需要引入其他的系統庫,只要在數組中直接增加即可。再定義一個native的方法:
public static native void callLogFromJni();
在Jni中調用Log庫的方法:
//引入 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"); }
第一個參數, ANDROID_LOG_INFO 是log的級別他包含:
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;
一般我們常用的是 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 。
第二個參數是tag,用來方便的對log分類。
第三個參數是message,對應log的具體信息。
一般還會采用宏定義的方式,定義Log的輸出的方法,方便調用:
#define LOG_TAG "jni-log" #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
這里定義了一個warning log的宏,在代碼里面可以直接調用:
LOGW("log from define");
使用第三方類庫
OpenSSL是最常用的加密庫之一,下面以OpenSSL為例,介紹下在 gradle-experimental 中如何引入第三方類庫。關于如何編譯Android下的OpenSSL詳見: 編譯Android的OpenSSL類庫 。
首先定義對于庫的 repositories :
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") // } } } } }
c語言的類庫分為靜態鏈接庫(.a)和動態鏈接庫(.so),靜態類庫和動態類庫的引入方式是不一樣的,分為對應: StaticLibraryBinary 和 SharedLibraryBinary 。這里引入的庫為靜態鏈接庫,名稱為: openssl .
指定庫依賴:
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" } } } } } }
這里在model.android.sources.main中指定庫的依賴為上面定義的 openssl , linkage 類型為static,如果是動態鏈接庫 linkage 就是shared。
因為編譯的OpenSSL只支持arm結構的cpu,因此需要指定abi為對應的cpu,在model.android添加配置:
ndk { moduleName "experiment" abiFilters.addAll(['armeabi', 'armeabi-v7a']) }
使用OpenSSL
首先定義一個 native 方法,需要從OpenSSL中讀取隨機數:
public static native byte[] getRandom();
生成對應的JNI方法:
//引入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; }
RAND_seed 是OpenSSL的方法,讀取隨機數。這段代碼就是讀取一個128的隨機數,然后轉換為java的byte[]。
在界面上面使用讀取隨機數的方法:
tv2.setText(Base64.encodeToString(NativeUtil.getRandom(), Base64.DEFAULT));
運行之后可以在界面看到一段隨機的字符串顯示:
源代碼地址: https://github.com/jjz/android/tree/master/experimental
來自: https://segmentfault.com/a/1190000005752063