Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android NDK下log的使用和封裝

android NDK下log的使用和封裝

編輯:關於Android編程

####################################################

 

使用NDK,就一定會接觸到log的使用。已經接觸了一段時間的NDK開發,也對log的使用做了一些總結,但基本上都是學習別人的方法,在自己實際使用的過程中,並不能趁心如意。所以,這一次對log的使用做一個全面的學習和復習。

 

####################################################

 

如何將so庫裡面的信息輸出到logcat中?可以使用android/log.h中的語句進行輸出

讓我們先看看log.h中有哪些需要了解的地方

 

log.h文件一般在$(NDK_HOME)/platforms/android-*/arch-*/usr/include/android之中

雖然有很多版本的log.h文件,但是沒有任何變化

基本內容如下:

1.NDK log支持在運行時發送信息到android內核日志緩沖區,這樣就可以通過logcat進行訪問

2.每個日志消息必須包含3部分:優先等級(priority),日志標簽(log tag)以及信息(text)

3.標簽相對應於發送日志信息的組件

4.如果日志消息文本過大,可能會被截斷。日志消息文本的的長度會有限制,比如最大1023個字節(下面會有測試

5.如果日志消息的末尾沒有換行符,那麼會自動添加一個換行符(下面會有測試)。所以,想要多次輸出信息,但在logcat上顯示在同一行是不可能的。

 

同時log.h文件中也提醒我們適度使用日志,它給出了3條理由:

1)發送日志消息會使用CPU,這樣會減慢你的應用程序和系統;

2)循環日志緩沖區非常的小(<64KB),所以如果你發送了大量的日志信息,那麼可能會影響系統以及應用程序的一些重要的日志信息的推送;

3)在正式版本中,不應該發送任何日志信息,除非在一些特殊情況下。

 

下面來看一看具體的實現代碼:

對於優先等級,log.h中定義了一個枚舉:

 

/*
 * Android log priority values, in ascending priority order.
 */
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;

同時它聲明了4個函數,通常我在使用過程中,僅需其中一個函數即可:

 

 

/*
 * 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
    ;
函數功能:發送一條格式化的字符串,使用方法就像C語言中的printf函數。

 

參數:

1.prio-int類型,優先等級,使用枚舉android_LogPriority中的定義

2.tag-const char*類型,標簽,對應於發送消息的組件

3.fmt-const char*類型,格式化字符串,例如:“hi, %s”

4....-占位符,輸出日志信息

 

note:如果你使用GNUC編譯器的話,還會使用__attribute__,這裡不多介紹,感興趣的可以查找資料

 

既然log.h文件已經大致介紹完畢,那麼開始一個小小的demo來使用一下:

本次demo實現功能:

1)連續2次輸出文本“Hello, World”,判斷是否會自動添加換行符;

2)輸出文本“Hi, zj\n”,判斷有了換行符後,是否還會添加;

3)輸出文本“One, Two\nThree, Four”,判斷中間有換行符後是否會截斷輸出;

4)輸出1200個數字,從0開始,每100個加1,判斷文本是否會被截斷;

 

制作so庫的方法:android NDK JNI so文件的制作和使用-http://blog.csdn.net/u012005313/article/details/52005958

 

下面貼出主要代碼:

MainActivity:

 

package com.zj.logdemo;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends Activity {

    static {
        System.loadLibrary("LogDemo");
    }

    public native void test();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        test();
    }
}

 

 

com_zj_logdemo_MainActivity.h:

 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_zj_logdemo_MainActivity */

#ifndef _Included_com_zj_logdemo_MainActivity
#define _Included_com_zj_logdemo_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
#undef com_zj_logdemo_MainActivity_BIND_ABOVE_CLIENT
#define com_zj_logdemo_MainActivity_BIND_ABOVE_CLIENT 8L
#undef com_zj_logdemo_MainActivity_BIND_ADJUST_WITH_ACTIVITY
#define com_zj_logdemo_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L
#undef com_zj_logdemo_MainActivity_BIND_ALLOW_OOM_MANAGEMENT
#define com_zj_logdemo_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
#undef com_zj_logdemo_MainActivity_BIND_AUTO_CREATE
#define com_zj_logdemo_MainActivity_BIND_AUTO_CREATE 1L
#undef com_zj_logdemo_MainActivity_BIND_DEBUG_UNBIND
#define com_zj_logdemo_MainActivity_BIND_DEBUG_UNBIND 2L
#undef com_zj_logdemo_MainActivity_BIND_EXTERNAL_SERVICE
#define com_zj_logdemo_MainActivity_BIND_EXTERNAL_SERVICE -2147483648L
#undef com_zj_logdemo_MainActivity_BIND_IMPORTANT
#define com_zj_logdemo_MainActivity_BIND_IMPORTANT 64L
#undef com_zj_logdemo_MainActivity_BIND_NOT_FOREGROUND
#define com_zj_logdemo_MainActivity_BIND_NOT_FOREGROUND 4L
#undef com_zj_logdemo_MainActivity_BIND_WAIVE_PRIORITY
#define com_zj_logdemo_MainActivity_BIND_WAIVE_PRIORITY 32L
#undef com_zj_logdemo_MainActivity_CONTEXT_IGNORE_SECURITY
#define com_zj_logdemo_MainActivity_CONTEXT_IGNORE_SECURITY 2L
#undef com_zj_logdemo_MainActivity_CONTEXT_INCLUDE_CODE
#define com_zj_logdemo_MainActivity_CONTEXT_INCLUDE_CODE 1L
#undef com_zj_logdemo_MainActivity_CONTEXT_RESTRICTED
#define com_zj_logdemo_MainActivity_CONTEXT_RESTRICTED 4L
#undef com_zj_logdemo_MainActivity_MODE_APPEND
#define com_zj_logdemo_MainActivity_MODE_APPEND 32768L
#undef com_zj_logdemo_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING
#define com_zj_logdemo_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING 8L
#undef com_zj_logdemo_MainActivity_MODE_MULTI_PROCESS
#define com_zj_logdemo_MainActivity_MODE_MULTI_PROCESS 4L
#undef com_zj_logdemo_MainActivity_MODE_NO_LOCALIZED_COLLATORS
#define com_zj_logdemo_MainActivity_MODE_NO_LOCALIZED_COLLATORS 16L
#undef com_zj_logdemo_MainActivity_MODE_PRIVATE
#define com_zj_logdemo_MainActivity_MODE_PRIVATE 0L
#undef com_zj_logdemo_MainActivity_MODE_WORLD_READABLE
#define com_zj_logdemo_MainActivity_MODE_WORLD_READABLE 1L
#undef com_zj_logdemo_MainActivity_MODE_WORLD_WRITEABLE
#define com_zj_logdemo_MainActivity_MODE_WORLD_WRITEABLE 2L
#undef com_zj_logdemo_MainActivity_DEFAULT_KEYS_DIALER
#define com_zj_logdemo_MainActivity_DEFAULT_KEYS_DIALER 1L
#undef com_zj_logdemo_MainActivity_DEFAULT_KEYS_DISABLE
#define com_zj_logdemo_MainActivity_DEFAULT_KEYS_DISABLE 0L
#undef com_zj_logdemo_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL
#define com_zj_logdemo_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
#undef com_zj_logdemo_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL
#define com_zj_logdemo_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
#undef com_zj_logdemo_MainActivity_DEFAULT_KEYS_SHORTCUT
#define com_zj_logdemo_MainActivity_DEFAULT_KEYS_SHORTCUT 2L
#undef com_zj_logdemo_MainActivity_RESULT_CANCELED
#define com_zj_logdemo_MainActivity_RESULT_CANCELED 0L
#undef com_zj_logdemo_MainActivity_RESULT_FIRST_USER
#define com_zj_logdemo_MainActivity_RESULT_FIRST_USER 1L
#undef com_zj_logdemo_MainActivity_RESULT_OK
#define com_zj_logdemo_MainActivity_RESULT_OK -1L
/*
 * Class:     com_zj_logdemo_MainActivity
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zj_logdemo_MainActivity_test
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

main.cpp:

 

 

#include 
#include 
#include 
#include 
#include "com_zj_logdemo_MainActivity.h"
using namespace std;

extern "C"
{
/*
 * Class:     com_zj_logdemo_MainActivity
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zj_logdemo_MainActivity_test
  (JNIEnv *env, jobject obj)
  {
    __android_log_print(ANDROID_LOG_INFO, "zj", "%s", "Hello, World");
    __android_log_print(ANDROID_LOG_INFO, "zj", "%s", "Hello, World");

    __android_log_print(ANDROID_LOG_INFO, "zj", "%s", "Hi,zj\n");

    __android_log_print(ANDROID_LOG_INFO, "zj", "%s", "One, Two\nThree, Four");

    stringstream ss;
    for (int i=0; i<1200; i++)
    {
        ss << i/100;
    }
    __android_log_print(ANDROID_LOG_INFO, "zj", "%s", ss.str().c_str());
  }
}

Android.mk:

 

 

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := LogDemo
LOCAL_SRC_FILES := main.cpp
LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lm -llog
include $(BUILD_SHARED_LIBRARY)

Application.mk:

 

 

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a       #這句是設置生成的cpu指令類型,提示,目前絕大部分安卓手機支持armeabi,libs下太多類型,編譯進去 apk 包會過大
APP_PLATFORM := android-8    #這句是設置最低安卓平台,可以不弄

注意:要想在NDK中使用log,必須在Android.mk中加入LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -llog

 

輸出:

\

 

\

根據顯示的結果可知:

1)如果信息末尾沒有換行符,那麼會自動添加;同時信息中間出現換行符不影響末尾自動添加。

2)文本過長會有截斷,說明對於每次發送的信息有長度限制。

 

####################################################################

 

上一步我們已經成功實現了NDK log日志的輸出,但是顯而易見的,直接調用log.h的函數進行日志輸出比較麻煩。網上流傳著一組宏定義:

 

#define TAG "zj"  
  
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)  
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)  
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)  
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)  
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 

之後想要進行log輸出即可直接使用宏定義函數:

 

修改main.cpp:

 

#include 
#include 
#include 
#include 
#include "com_zj_logdemo_MainActivity.h"
using namespace std;

extern "C"
{
#define TAG "zj"

#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

/*
 * Class:     com_zj_logdemo_MainActivity
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zj_logdemo_MainActivity_test
  (JNIEnv *env, jobject obj)
  {
    LOGI("Hello everybody");
    LOGD("%d + %d = %d", 1, 1, 2);
  }
}

結果:

 

\

 

這樣,coding的效率就提高了

################################################################3

 

就像log.h裡面說的,當你進行開發的時候,可能需要很多的日志輸出,但當你發布正式版本時,就應該盡量避免日志的輸出。

如果使用上述方法,在發布正式版本的時候必須逐行注釋掉日志,耗費時間。同時,如果在運行過程中出現問題,又必須重新輸出日志,不利於開發。可以對上述代碼再做一次封裝:

 

#if 1
#define log_print_verbose(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define log_print_debug(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define log_print_info(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define log_print_warn(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define log_print_error(...) __android_log_print(ANROID_LOG_ERROR, TAG, __VA_ARGS__)
#else
#define log_print_verbose(...)
#define log_print_debug(...)
#define log_print_info(...)
#define log_print_warn(...)
#define log_print_error(...)
#endif

#define LOGV(...) log_print_verbose(__VA_ARGS__)
#define LOGD(...) log_print_debug(__VA_ARGS__)
#define LOGI(...) log_print_info(__VA_ARGS__)
#define LOGW(...) log_print_warn(__VA_ARGS__)
#define LOGE(...) log_print_error(__VA_ARGS__)

進行日志輸出

 

main.cpp如下:

 

#include 
#include 
#include 
#include 
#include "com_zj_logdemo_MainActivity.h"
using namespace std;

extern "C"
{

#define TAG "test"

#if 1
#define log_print_verbose(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define log_print_debug(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define log_print_info(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define log_print_warn(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define log_print_error(...) __android_log_print(ANROID_LOG_ERROR, TAG, __VA_ARGS__)
#else
#define log_print_verbose(...)
#define log_print_debug(...)
#define log_print_info(...)
#define log_print_warn(...)
#define log_print_error(...)
#endif

#define LOGV(...) log_print_verbose(__VA_ARGS__)
#define LOGD(...) log_print_debug(__VA_ARGS__)
#define LOGI(...) log_print_info(__VA_ARGS__)
#define LOGW(...) log_print_warn(__VA_ARGS__)
#define LOGE(...) log_print_error(__VA_ARGS__)


/*
 * Class:     com_zj_logdemo_MainActivity
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zj_logdemo_MainActivity_test
  (JNIEnv *env, jobject obj)
  {

    LOGI("Hello everybody");
    LOGD("%d + %d = %d", 1, 1, 2);
  }
}

 

 

結果:

\

 

發布正式版本

main.cpp如下:

 

#include 
#include 
#include 
#include 
#include "com_zj_logdemo_MainActivity.h"
using namespace std;

extern "C"
{

#define TAG "zj"

#if 0
#define log_print_verbose(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define log_print_debug(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define log_print_info(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define log_print_warn(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define log_print_error(...) __android_log_print(ANROID_LOG_ERROR, TAG, __VA_ARGS__)
#else
#define log_print_verbose(...)
#define log_print_debug(...)
#define log_print_info(...)
#define log_print_warn(...)
#define log_print_error(...)
#endif

#define LOGV(...) log_print_verbose(__VA_ARGS__)
#define LOGD(...) log_print_debug(__VA_ARGS__)
#define LOGI(...) log_print_info(__VA_ARGS__)
#define LOGW(...) log_print_warn(__VA_ARGS__)
#define LOGE(...) log_print_error(__VA_ARGS__)


/*
 * Class:     com_zj_logdemo_MainActivity
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zj_logdemo_MainActivity_test
  (JNIEnv *env, jobject obj)
  {

    LOGI("Hello everybody");
    LOGD("%d + %d = %d", 1, 1, 2);
  }
}

結果:不顯示任何日志

 

 

####################################################################

 

在使用過程中,可能你想要把代碼移植到C/C++工程中,但是無法使用__android_log_print函數,所以必須再次封裝:、

新建文件logUtil.hpp

 

#ifndef LOGUTIL_HPP
#define LOGUTIL_HPP

#define NDK_LOG false

#if NDK_LOG
#include 
#include 
#endif

#define TAG "zj"

#if NDK_LOG
#define log_print_verbose(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, fmt, __VA_ARGS__)
#define log_print_debug(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, __VA_ARGS__)
#define log_print_info(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, __VA_ARGS__)
#define log_print_warn(fmt, ...) __android_log_print(ANDROID_LOG_WARN, TAG, fmt, __VA_ARGS__)
#define log_print_error(fmt, ...) __android_log_print(ANROID_LOG_ERROR, TAG, fmt, __VA_ARGS__)
#else
#define log_print_verbose(fmt, ...)  printf(fmt, ##__VA_ARGS__)
#define log_print_debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define log_print_info(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define log_print_warn(fmt, ...)  printf(fmt, ##__VA_ARGS__)
#define log_print_error(fmt, ...) printf(fmt,  ##__VA_ARGS__)
#endif

#define LOGV(fmt, ...) log_print_verbose(fmt, ##__VA_ARGS__)
#define LOGD(fmt, ...) log_print_debug(fmt, ##__VA_ARGS__)
#define LOGI(fmt, ...) log_print_info(fmt, ##__VA_ARGS__)
#define LOGW(fmt, ...) log_print_warn(fmt, ##__VA_ARGS__)
#define LOGE(fmt, ...) log_print_error(fmt, ##__VA_ARGS__)

#endif // LOGUTIL_HPP

這樣就可以在任何情況下移植代碼,而且方便日志的記錄和發送。

 

下面測試一個小demo:

新建一個qt工程-qt_log

新建main.cpp:

 

#include 
#include "logutil.hpp"
using namespace std;

int main(int argc, char* argv[])
{
    LOGI("Hello World\n");
    LOGW("Hi %s\n", "zj");

    return 0;
}

 

結果:

\

 

運行成功

注意:在C++項目中,無法實現自動換行功能。所以在每條日志的第一條字符串末尾必須顯式加入'\n'換行符。

 

#########################################################

 

同樣的,在C/C++項目中,我們也想要在正式版本中不輸出日志,所以最終的logUtil.hpp可以修改如下:

 

#ifndef LOGUTIL_HPP
#define LOGUTIL_HPP

#define NDK_LOG false
#define C_LOG true

#if NDK_LOG
#include 
#include 
#endif

#define TAG "zj"

#if NDK_LOG
#define log_print_verbose(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, fmt, __VA_ARGS__)
#define log_print_debug(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, __VA_ARGS__)
#define log_print_info(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, __VA_ARGS__)
#define log_print_warn(fmt, ...) __android_log_print(ANDROID_LOG_WARN, TAG, fmt, __VA_ARGS__)
#define log_print_error(fmt, ...) __android_log_print(ANROID_LOG_ERROR, TAG, fmt, __VA_ARGS__)
#elif C_LOG
#define log_print_verbose(fmt, ...)  printf(fmt, ##__VA_ARGS__)
#define log_print_debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define log_print_info(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define log_print_warn(fmt, ...)  printf(fmt, ##__VA_ARGS__)
#define log_print_error(fmt, ...) printf(fmt,  ##__VA_ARGS__)
#else
#define log_print_verbose(fmt, ...)
#define log_print_debug(fmt, ...)
#define log_print_info(fmt, ...)
#define log_print_warn(fmt, ...)
#define log_print_error(fmt, ...)
#endif


#define LOGV(fmt, ...) log_print_verbose(fmt, ##__VA_ARGS__)
#define LOGD(fmt, ...) log_print_debug(fmt, ##__VA_ARGS__)
#define LOGI(fmt, ...) log_print_info(fmt, ##__VA_ARGS__)
#define LOGW(fmt, ...) log_print_warn(fmt, ##__VA_ARGS__)
#define LOGE(fmt, ...) log_print_error(fmt, ##__VA_ARGS__)


#endif // LOGUTIL_HPP
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved