C語言
是一個巨大的寶庫,Android是一個以Linux爲基礎的開源操做系統,系統底層不少的實現都是基於C語言
開發,好比圖像處理,加密等。另外一方面C語言
的運行效率也比Java開發要高不少,所以爲了高效率的運行有時候也會使用C語言
開發一些功能。再Android上面使用C語言
開發就須要使用NDK,在使用NDK開發的過程當中會使用大量的庫,系統自帶的庫,第三方庫以及本身寫的庫等。
使用Android Studio
調用NDK的庫是很是簡便,NDK內置了一些庫方便開發者使用好比:Log庫
,還有一些比較經常使用的第三方庫好比:OpenSSL
。下面會分別介紹下,這兩種庫的使用。javascript
Log
是在Android開發過程用來調試程序必備的工具之一,他會把日誌信息輸入到Logcat
中,如何在NDK中使用android.util.Log
方便在Logcat
中查看JNI程序的運行狀況呢?
這就須要在NDK開發中導入Android系統的Log
庫。首先須要在gradle
中引入Log庫,引入的方式使用是在ldLibs
中添加:java
model{
....
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
ndk {
moduleName "experiment"
ldLibs.addAll([ 'log']);
}
}
}複製代碼
直接gradle中的ldLIbs
中加入log就能夠了,若是還須要引入其餘的系統庫,只要在數組中直接增長便可。
下面來測試下log庫
的使用,先定義一個native
的方法:android
public static native void callLogFromJni();git
在JNI中調用Log庫:github
//引入 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的具體信息。dom
通常會採用宏定義的方式,定義Log的輸出的方法,方便調用,例如:工具
#define LOG_TAG "jni-log"
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)複製代碼
這裏定義了一個warning log
的宏,在代碼裏面能夠直接調用:post
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
不管是調用系統庫仍是第三方庫都比較簡單。