android NDK下log的使用和封装

  • Post author:
  • Post category:其他


参考:

Android Studio 上如何使用LogCat:

http://blog.csdn.net/u012005313/article/details/46928027

Android log日志记录方法:

http://blog.csdn.net/u012005313/article/details/47747997

android ndk 入门3 – log实现:

http://blog.csdn.net/u012005313/article/details/50052115

Android 定制自己的日志工具 《第一行代码》:

http://blog.csdn.net/u012005313/article/details/49589641

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

使用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 <jni.h>
/* 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 <iostream>
#include <sstream>
#include <jni.h>
#include <android/log.h>
#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 <iostream>
#include <sstream>
#include <jni.h>
#include <android/log.h>
#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 <iostream>
#include <sstream>
#include <jni.h>
#include <android/log.h>
#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 <iostream>
#include <sstream>
#include <jni.h>
#include <android/log.h>
#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 <android/log.h>
#include <jni.h>
#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 <iostream>
#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 <android/log.h>
#include <jni.h>
#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



版权声明:本文为u012005313原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。