参考:
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