C語言
是一個巨大的寶庫,Android是一個以Linux為基礎的開源作業系統,系統底層很多的實現都是基於C語言
開發,比如影像處理,加密等。另一方面C語言
的執行效率也比Java開發要高很多,因此為了高效率的執行有時候也會使用C語言
開發一些功能。再Android上面使用C語言
開發就需要使用NDK,在使用NDK開發的過程中會使用大量的庫,系統自帶的庫,第三方庫以及自己寫的庫等。
使用Android Studio
呼叫NDK的庫是非常簡便,NDK內建了一些庫方便開發者使用比如:Log庫
,還有一些比較常用的第三方庫比如:OpenSSL
。下面會分別介紹下,這兩種庫的使用。
呼叫系統庫
Log
是在Android開發過程用來除錯程式必備的工具之一,他會把日誌資訊輸入到Logcat
中,如何在NDK中使用android.util.Log
方便在Logcat
中檢視JNI程式的執行情況呢?
這就需要在NDK開發中匯入Android系統的Log
庫。首先需要在gradle
中引入Log庫,引入的方式使用是在ldLibs
中新增:
model{
....
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
ndk {
moduleName "experiment"
ldLibs.addAll([ `log`]);
}
}
}複製程式碼
直接gradle中的ldLIbs
中加入log就可以了,如果還需要引入其他的系統庫,只要在陣列中直接增加即可。
下面來測試下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
->Log.vANDROID_LOG_DEBUG
->Log.dANDROID_LOG_INFO
-> Log.iANDROID_LOG_WARN
->Log.wANDROID_LOG_ERROR
->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");複製程式碼
系統庫的呼叫比較簡單方便,使用第三方庫就比較麻煩些,第三方庫需要使用NDK重新編譯才能在JNI中呼叫。
呼叫第三方類庫
OpenSSL
是最常用的加密庫,下面就以OpenSSL
為例,介紹下在gradle-experimental
中如何引入第三方類庫。
關於如何編譯Android
下的OpenSSL
詳見:Andorid Studio NDK 開發 – 編譯 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
-> 動態連結庫
這裡引入的庫為靜態連結庫,庫的repositories名稱為:openssl
.
定義好了一個repositories
,現在就需要呼叫了,在gradle
可以指定庫的依賴:
model{
......
android{
.....
sources {
main {
jni {
dependencies{
//靜態連結庫
library `openssl` linkage `static`
//動態連結庫
// library `openssl` linkage `shared`
}
source {
srcDir "src/main/jni"
}
}
jniLibs{
source{
srcDir "libs/"
}
}
....
}
}
}
}複製程式碼
在model.android.sources.main
中指定庫的依賴,依賴的是上面定義的openssl
,linkage
型別為static,如果是動態連結庫則linkage
就是shared。
因為在編譯OpenSSL
設定了只支援arm
結構的cpu,所以還需要指定abi
為對應為arm架構,在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];
//使用OpenSSL的方法
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));複製程式碼
執行之後可以在介面看到一段隨機的字串顯示:
可以看到使用gradle-experiment
無論是呼叫系統庫還是第三方庫都比較簡單。
以上原始碼地址:github.com/jjz/android…