C語言是一個巨大的寶庫,系統底層的很多的實現都是基於C語言實現的,比如影像處理,加密等。C語言的執行效率也是很高的,因此為了效率有時候也會引入第三方的C語言庫。
總而言之,會在NDK開發的過程中會使用大量的庫,系統自帶的庫,第三方庫等。在gradle-experimental
中使用C語言的庫是非常便利的。
呼叫系統庫
Log是在Android開發過程用來除錯程式必備的工具之一,如何在NDK中使用android.util.Log
方便在Logcat中檢視JNI程式的執行情況呢?這就需要在NDK中匯入Android系統的Log
庫。
首先需要在在gradle
中引入Log庫:
1 2 3 4 5 6 7 8 9 10 11 |
model{ .... android { compileSdkVersion 23 buildToolsVersion "23.0.2" ndk { moduleName "experiment" ldLibs.addAll([ 'log']); } } } |
直接在ldLIbs中加入log就可以,如果需要引入其他的系統庫,只要在陣列中直接增加即可。
再定義一個native的方法:
public static native void callLogFromJni();
在Jni中呼叫Log庫的方法:
1 2 3 4 5 6 7 8 9 |
//引入 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的級別他包含:
1 2 3 4 5 6 7 8 9 10 11 |
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的輸出的方法,方便呼叫:
1 2 |
#define LOG_TAG "jni-log" #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) |
這裡定義了一個warning log的巨集,在程式碼裡面可以直接呼叫:
1 |
LOGW("log from define"); |
使用第三方類庫
OpenSSL是最常用的加密庫之一,下面以OpenSSL為例,介紹下在gradle-experimental
中如何引入第三方類庫。關於如何編譯Android下的OpenSSL詳見:編譯Android的OpenSSL類庫。
首先定義對於庫的repositories
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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
.
指定庫依賴:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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新增配置:
1 2 3 4 |
ndk { moduleName "experiment" abiFilters.addAll(['armeabi', 'armeabi-v7a']) } |
使用OpenSSL
首先定義一個native
方法,需要從OpenSSL中讀取隨機數:
1 |
public static native byte[] getRandom(); |
生成對應的JNI方法:
1 2 3 4 5 6 7 8 9 10 11 |
//引入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[]。
在介面上面使用讀取隨機數的方法:
1 |
tv2.setText(Base64.encodeToString(NativeUtil.getRandom(), Base64.DEFAULT)); |
執行之後可以在介面看到一段隨機的字串顯示:
原始碼地址:https://github.com/jjz/android/tree/master/experimental