Log是开发过程中,对于我们调试程序非常重要的一个工具,有很多时候,我们正是通过Log才能够看清楚程序是不是真的按照我们想像中的模式在跑,从而定位到问题所在的地方。而在Android开发中,毫无疑问,要是没有了logcat,我们调试程序的时候,就会痛苦死。
在NDK的开发中,尽管我们是利用C/C++来开发程序的,然后通过加载共享库的方法来调用C/C++程序,Android也提供了一套方法,可以让我们在LogCat中看到在C/C++代码中的数据流向,帮我们定位问题。
这一篇文章就简单地来说一下,如何在JNI层使用log工具。
回到我们之前的demo中,我们在程序中为了查看在JNI层某个数的值是否被改变了,我们特意添加了以下的log:
LOGI("before change testval = %d", val);
val = val + 1;
LOGI("after change testval = %d", val);
当程序在手机上运行的时候,我们就可以在LogCat中看到以下的记录:
这说明了,在C/C++中同样是可以利用log工具来调试的(其实这是废话,因为Android中Log的实现本来就是通过JNI层,由C++实现的。)
下面我们就来说一下,如何在C/C++文件中添加log吧。
1)在C/C++文件中,要添加log的引用文件,如下面第2行:
#include "com_lms_jni_ParamTransferTest.h"
#include <android/log.h>
#include <jni.h>
在这里,我们会引用android/log.h头文件,而log.h头文件定义了几个log函数,如下:
/*
* 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);
2)引入log.h头文件之后,我们可以在C/C++中直接使用__android_log_print方法,不过老这样使用,就太麻烦了,所以我们可以重新定义一下,如下:
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
在这里,我们定义了LOGD和LOGI 可变参数宏,分别对应不同级别的__andoid_log_print函数,这样我们在代码中,就可以直接通过LOGD和LOGI来写入log信息了。
在log.h头文件中,我们可以查到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;
3)接下来,就是如何使用LOGI或者LOGD了,如下,在程序中需要写入log的地方,调用函数:
JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal
(JNIEnv * env, jobject obj){
jclass clazz = (*env)->GetObjectClass(env,obj);
jint val = (*env)->GetStaticIntField(env, clazz,
(*env)->GetStaticFieldID(env, clazz,"testval","I"));
LOGI("before change testval = %d", val);
val = val + 1;
LOGI("after change testval = %d", val);
(*env)->SetStaticIntField(env, clazz,(*env)->GetStaticFieldID(env, clazz,"testval","I"),val);
}
4)到这一步,在C/C++中的使用就结束了,但是JNI的使用一般都是通过编译成共享库的形式,所以我们还需要在Android.mk文件中指定对应的库文件,如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_lms_jni_HwDemo
LOCAL_SRC_FILES := \
HwDemo.c \
JniTest.c \
ParamTransferTest.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
其中,我们可以看到 LOCAL_LDLIBS += -llog,在这里,LOCAL_LDLIBS 是告诉编译器,在编译这个共享库的时候,我们要去链接系统库中某一个库,而-llog,其实就是代表
liblog库的意思,-l是表明lib,而log则是表明前缀是lib的liblog的库,跟LOCAL_MODULE一样,编译器和链接器会自动处理前缀lib跟后缀.so,而liblog.so这个库就是在ndk提供的系统库中,如下:
5)通过这几步,我们就可以像在Android调试一样,在C/C++中去调试我们的程序了。
结束。