Android应用程序框架层和系统运行库层日志系统源代码分析
来源:互联网 发布:recscreen录屏软件 编辑:程序博客网 时间:2024/06/15 20:50
在开发Android应用程序时,少不了使用Log来监控和调试程序的执行。在上一篇文章Android日志系统驱动程序Logger源代码分析中,我们分析了驱动程序Logger的源代码,在前面的文章浅谈Android系统开发中Log的使用一文,我们也简单介绍在应用程序中使Log的方法,在这篇文章中,我们将详细介绍android应用程序框架层和系统运行库存层日志系统的源代码,使得我们可以更好地理解Android的日志系统的实现。
《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
我们在Android应用程序,一般是调用应用程序框架层的Java接口(android.util.Log)来使用日志系统,这个Java接口通过JNI方法和系统运行库最终调用内核驱动程序Logger把Log写到内核空间中。按照这个调用过程,我们一步步介绍Android应用程序框架层日志系统的源代码。学习完这个过程之后,我们可以很好地理解Android系统的架构,即应用程序层(Application)的接口是如何一步一步地调用到内核空间的。
一. 应用程序框架层日志系统Java接口的实现。
在浅谈Android系统开发中Log的使用一文中,我们曾经介绍过Android应用程序框架层日志系统的源代码接口。这里,为了描述方便和文章的完整性,我们重新贴一下这部份的代码,在frameworks/base/core/java/android/util/Log.java文件中,实现日志系统的Java接口:
- ................................................
-
- public final class Log {
-
- ................................................
-
-
-
-
- public static final int VERBOSE = 2;
-
-
-
-
- public static final int DEBUG = 3;
-
-
-
-
- public static final int INFO = 4;
-
-
-
-
- public static final int WARN = 5;
-
-
-
-
- public static final int ERROR = 6;
-
-
-
-
- public static final int ASSERT = 7;
-
- .....................................................
-
- public static int v(String tag, String msg) {
- return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
- }
-
- public static int v(String tag, String msg, Throwable tr) {
- return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
- }
-
- public static int d(String tag, String msg) {
- return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
- }
-
- public static int d(String tag, String msg, Throwable tr) {
- return println_native(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
- }
-
- public static int i(String tag, String msg) {
- return println_native(LOG_ID_MAIN, INFO, tag, msg);
- }
-
- public static int i(String tag, String msg, Throwable tr) {
- return println_native(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));
- }
-
- public static int w(String tag, String msg) {
- return println_native(LOG_ID_MAIN, WARN, tag, msg);
- }
-
- public static int w(String tag, String msg, Throwable tr) {
- return println_native(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr));
- }
-
- public static int w(String tag, Throwable tr) {
- return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
- }
-
- public static int e(String tag, String msg) {
- return println_native(LOG_ID_MAIN, ERROR, tag, msg);
- }
-
- public static int e(String tag, String msg, Throwable tr) {
- return println_native(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
- }
-
- ..................................................................
- public static native int LOG_ID_MAIN = 0;
- public static native int LOG_ID_RADIO = 1;
- public static native int LOG_ID_EVENTS = 2;
- public static native int LOG_ID_SYSTEM = 3;
-
- public static native int println_native(int bufID,
- int priority, String tag, String msg);
- }
定义了2~7一共6个日志优先级别ID和4个日志缓冲区ID。回忆一下Android日志系统驱动程序Logger源代码分析一文,在Logger驱动程序模块中,定义了log_main、log_events和log_radio三个日志缓冲区,分别对应三个设备文件/dev/log/main、/dev/log/events和/dev/log/radio。这里的4个日志缓冲区的前面3个ID就是对应这三个设备文件的文件描述符了,在下面的章节中,我们将看到这三个文件描述符是如何创建的。在下载下来的Android内核源代码中,第4个日志缓冲区LOG_ID_SYSTEM并没有对应的设备文件,在这种情况下,它和LOG_ID_MAIN对应同一个缓冲区ID,在下面的章节中,我们同样可以看到这两个ID是如何对应到同一个设备文件的。 在整个Log接口中,最关键的地方声明了println_native本地方法,所有的Log接口都是通过调用这个本地方法来实现Log的定入。下面我们就继续分析这个本地方法println_native。
二. 应用程序框架层日志系统JNI方法的实现。
在frameworks/base/core/jni/android_util_Log.cpp文件中,实现JNI方法println_native:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #define LOG_NAMESPACE "log.tag."
- #define LOG_TAG "Log_println"
-
- #include <assert.h>
- #include <cutils/properties.h>
- #include <utils/Log.h>
- #include <utils/String8.h>
-
- #include "jni.h"
- #include "utils/misc.h"
- #include "android_runtime/AndroidRuntime.h"
-
- #define MIN(a,b) ((a<b)?a:b)
-
- namespace android {
-
- struct levels_t {
- jint verbose;
- jint debug;
- jint info;
- jint warn;
- jint error;
- jint assert;
- };
- static levels_t levels;
-
- static int toLevel(const char* value)
- {
- switch (value[0]) {
- case 'V': return levels.verbose;
- case 'D': return levels.debug;
- case 'I': return levels.info;
- case 'W': return levels.warn;
- case 'E': return levels.error;
- case 'A': return levels.assert;
- case 'S': return -1;
- }
- return levels.info;
- }
-
- static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
- {
- #ifndef HAVE_ANDROID_OS
- return false;
- #else /* HAVE_ANDROID_OS */
- int len;
- char key[PROPERTY_KEY_MAX];
- char buf[PROPERTY_VALUE_MAX];
-
- if (tag == NULL) {
- return false;
- }
-
- jboolean result = false;
-
- const char* chars = env->GetStringUTFChars(tag, NULL);
-
- if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
- jclass clazz = env->FindClass("java/lang/IllegalArgumentException");
- char buf2[200];
- snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
- chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
-
-
- env->ReleaseStringUTFChars(tag, chars);
-
- env->ThrowNew(clazz, buf2);
- return false;
- } else {
- strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);
- strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);
- }
-
- env->ReleaseStringUTFChars(tag, chars);
-
- len = property_get(key, buf, "");
- int logLevel = toLevel(buf);
- return (logLevel >= 0 && level >= logLevel) ? true : false;
- #endif /* HAVE_ANDROID_OS */
- }
-
-
-
-
-
- static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
- jint bufID, jint priority, jstring tagObj, jstring msgObj) <li class="alt" style="border-top: none; border-right: none; border-bottom: none; border-left: 3px solid rgb(108, 226, 108); border-image: initial; list-style-type: decimal-leading-zero; list-style-image