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