Andorid Studio NDK開發-使用NDK庫

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.v
  • ANDROID_LOG_DEBUG->Log.d
  • ANDROID_LOG_INFO-> Log.i
  • ANDROID_LOG_WARN->Log.w
  • ANDROID_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));複製代碼

運行以後能夠在界面看到一段隨機的字符串顯示:

從openssl中讀取隨機數

能夠看到使用 gradle-experiment不管是調用系統庫仍是第三方庫都比較簡單。
以上源代碼地址: github.com/jjz/android…
相關文章
相關標籤/搜索