1. 程式人生 > >JNI學習二之(C原始碼中Log輸出及常見錯誤)

JNI學習二之(C原始碼中Log輸出及常見錯誤)

瞭解jni

JNI 即Java Native Interface ,Java本機介面。可以實現Java和C/C++之間的相互呼叫。
為什麼使用JNI?
擴充套件了Java虛擬機器的能力,C語言可以進行驅動開發,比如wifi共享熱點的驅動
Native code執行效率比較快,數學運算,實時渲染遊戲,音視訊處理等。Java的記憶體回收是基於演算法的,不受程式設計師的控制,而C語言的記憶體回收由程式設計師負責。
C語言產生於上世紀70年代,有很多可以複用的庫,比如ffmpeg(音視訊解碼),opencv(影象處理),opencore(音訊處理)等。

jni開發常見錯誤

  • 忘記方法的引數
  • java.lang.UnsatisfiedLinkError: Native method not found
    一般是c中的方法名寫的不正確
  • 一般沒有日誌列印 直接報錯工程停止 ,一般c程式碼有執行錯誤
  • 引用別人.so 函式庫 ,需要你自己native方法對應類的包名和別人打包成.so函式庫的包名一致

在C原始碼中輸出log

我們知道需要的jni.h檔案在ndk目錄的如下目,其中android 平臺的版本可任選

ndk-bundle\platforms\android-23\arch-arm\usr\include\jni.h

而關於log輸出的標頭檔案放在\include\android目錄下,即log.h

#ifndef _ANDROID_LOG_H
#define _ANDROID_LOG_H
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Android log priority values, in ascending priority order.
 */
//Log優先順序
typedef enum android_LogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
ANDROID_LOG_VERBOSE,//v ANDROID_LOG_DEBUG,//d ANDROID_LOG_INFO,//i ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ } android_LogPriority; /* * Send a simple string to the log. */ int __android_log_write(int prio, const char *tag, const char *text); /* * Send a formatted string to the log, used like printf(fmt,...) */ int __android_log_print(int prio, const char *tag, const char *fmt, ...) #if defined(__GNUC__) __attribute__ ((format(printf, 3, 4))) #endif ; /* * A variant of __android_log_print() that takes a va_list to list * additional parameters. */ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap); /* * Log an assertion failure and SIGTRAP the process to have a chance * to inspect it, if a debugger is attached. This uses the FATAL priority. */ void __android_log_assert(const char *cond, const char *tag, const char *fmt, ...) #if defined(__GNUC__) __attribute__ ((noreturn)) __attribute__ ((format(printf, 3, 4))) #endif ; #ifdef __cplusplus } #endif #endif /* _ANDROID_LOG_H */

其中關於輸出log的兩個函式時:

  //prio優先順序      tag和即標籤   
 int __android_log_write(int prio, const char *tag, const char *text);
 int __android_log_print(int prio, const char *tag,  const char *fmt, ...);

這兩個方法沒什麼大的區別,關鍵就在於第二個方法是可以傳入可變引數,比較靈活,其中…代表可變引數。就像printf函式那樣。
但是這連個函式的名字太長了,寫起來不太方便,我們就定義巨集來使用。

#include "com_pngfi_jnidemo_MainActivity.h"
#include <android/log.h>
#define TAG "clog"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)

JNIEXPORT jstring Java_com_pngfi_jnidemo_MainActivity_hello(JNIEnv *env, jobject obj) {
    int x=100;
    int y=105;
    LOGI("log in c %s %d","info",x);
    LOGD("log in c %s %d","debug",y);
    return (*(*env)).NewStringUTF(env, "hello jni");
}

這時候首先要匯入log.h標頭檔案,然後還需要引入庫,以前是在Android.mk檔案中新增,現在我們自己沒有寫.mk檔案,是讓AndroidStudio幫我們生成。那我們同樣需要在build.gradle檔案中defaultConfigs節點配置

//其中ldLibs 就是引入所需的庫
        ndk {
            moduleName "JniDemo"
            ldLibs "log", "z", "m"
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }

輸出結果如下:
這裡寫圖片描述