首頁(yè) 收藏 QQ群
 網(wǎng)站導(dǎo)航

ZNDS智能電視網(wǎng) 推薦當(dāng)貝市場(chǎng)

TV應(yīng)用下載 / 資源分享區(qū)

軟件下載 | 游戲 | 討論 | 電視計(jì)算器

綜合交流 / 評(píng)測(cè) / 活動(dòng)區(qū)

交流區(qū) | 測(cè)硬件 | 網(wǎng)站活動(dòng) | Z幣中心

新手入門(mén) / 進(jìn)階 / 社區(qū)互助

新手 | 你問(wèn)我答 | 免費(fèi)刷機(jī)救磚 | ROM固件

查看: 13048|回復(fù): 0
上一主題 下一主題
[教程]

Android應(yīng)用程序框架層和系統(tǒng)運(yùn)行庫(kù)層日志系統(tǒng)源代碼分析

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2013-8-28 16:26 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
在開(kāi)發(fā)Android應(yīng)用程序時(shí),少不了使用Log來(lái)監(jiān)控和調(diào)試程序的執(zhí)行。在上一篇文章Android日志系統(tǒng)驅(qū)動(dòng)程序Logger源代碼分析中,我們分析了驅(qū)動(dòng)程序Logger的源代碼,在前面的文章淺談Android系統(tǒng)開(kāi)發(fā)中Log的使用一文,我們也簡(jiǎn)單介紹在應(yīng)用程序中使Log的方法,在這篇文章中,我們將詳細(xì)介紹Android應(yīng)用程序框架層和系統(tǒng)運(yùn)行庫(kù)存層日志系統(tǒng)的源代碼,使得我們可以更好地理解Android的日志系統(tǒng)的實(shí)現(xiàn)。   
   
        我們?cè)贏ndroid應(yīng)用程序,一般是調(diào)用應(yīng)用程序框架層的Java接口(android.util.Log)來(lái)使用日志系統(tǒng),這個(gè)Java接口通過(guò)JNI方法和系統(tǒng)運(yùn)行庫(kù)最終調(diào)用內(nèi)核驅(qū)動(dòng)程序Logger把Log寫(xiě)到內(nèi)核空間中。按照這個(gè)調(diào)用過(guò)程,我們一步步介紹Android應(yīng)用程序框架層日志系統(tǒng)的源代碼。學(xué)習(xí)完這個(gè)過(guò)程之后,我們可以很好地理解Android系統(tǒng)的架構(gòu),即應(yīng)用程序?qū)樱ˋpplication)的接口是如何一步一步地調(diào)用到內(nèi)核空間的。   
   
        一. 應(yīng)用程序框架層日志系統(tǒng)Java接口的實(shí)現(xiàn)。   
   
        在淺談Android系統(tǒng)開(kāi)發(fā)中Log的使用一文中,我們?cè)?jīng)介紹過(guò)Android應(yīng)用程序框架層日志系統(tǒng)的源代碼接口。這里,為了描述方便和文章的完整性,我們重新貼一下這部份的代碼,在frameworks/base/core/java/android/util/Log.java文件中,實(shí)現(xiàn)日志系統(tǒng)的Java接口:   
  1. ................................................   
       
    public final class Log {   
       
    ................................................   
       
            /**   
             * Priority constant for the println method; use Log.v.   
             */   
            public static final int VERBOSE = 2;   
       
            /**   
             * Priority constant for the println method; use Log.d.   
             */   
            public static final int DEBUG = 3;   
       
            /**   
             * Priority constant for the println method; use Log.i.   
             */   
            public static final int INFO = 4;   
       
            /**   
             * Priority constant for the println method; use Log.w.   
             */   
            public static final int WARN = 5;   
       
            /**   
             * Priority constant for the println method; use Log.e.   
             */   
            public static final int ERROR = 6;   
       
            /**   
             * Priority constant for the println method.   
             */   
            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 +
  2. + 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 +
  3. + 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 +
  4. + 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 +
  5. + 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 +
  6. + getStackTraceString(tr));   
            }   
       
    ..................................................................   
            /** @hide */ public static native int LOG_ID_MAIN = 0;   
            /** @hide */ public static native int LOG_ID_RADIO = 1;   
            /** @hide */ public static native int LOG_ID_EVENTS = 2;   
            /** @hide */ public static native int LOG_ID_SYSTEM = 3;   
       
            /** @hide */ public static native int println_native(int bufID,   
                    int priority, String tag, String msg);   
    }
復(fù)制代碼
定義了2~7一共6個(gè)日志優(yōu)先級(jí)別ID和4個(gè)日志緩沖區(qū)ID。回憶一下Android日志系統(tǒng)驅(qū)動(dòng)程序Logger源代碼分析一文,在Logger驅(qū)動(dòng)程序模塊中,定義了log_main、log_events和log_radio三個(gè)日志緩沖區(qū),分別對(duì)應(yīng)三個(gè)設(shè)備文件/dev/log/main、/dev/log/events和/dev/log/radio。這里的4個(gè)日志緩沖區(qū)的前面3個(gè)ID就是對(duì)應(yīng)這三個(gè)設(shè)備文件的文件描述符了,在下面的章節(jié)中,我們將看到這三個(gè)文件描述符是如何創(chuàng)建的。在下載下來(lái)的Android內(nèi)核源代碼中,第4個(gè)日志緩沖區(qū)LOG_ID_SYSTEM并沒(méi)有對(duì)應(yīng)的設(shè)備文件,在這種情況下,它和LOG_ID_MAIN對(duì)應(yīng)同一個(gè)緩沖區(qū)ID,在下面的章節(jié)中,我們同樣可以看到這兩個(gè)ID是如何對(duì)應(yīng)到同一個(gè)設(shè)備文件的。   
         在整個(gè)Log接口中,最關(guān)鍵的地方聲明了println_native本地方法,所有的Log接口都是通過(guò)調(diào)用這個(gè)本地方法來(lái)實(shí)現(xiàn)Log的定入。下面我們就繼續(xù)分析這個(gè)本地方法println_native。   
   
         二. 應(yīng)用程序框架層日志系統(tǒng)JNI方法的實(shí)現(xiàn)。   
   
         在frameworks/base/core/jni/android_util_Log.cpp文件中,實(shí)現(xiàn)JNI方法println_native:   
  1. /* //device/libs/android_runtime/android_util_Log.cpp   
    **   
    ** Copyright 2006, The Android Open Source Project   
    **   
    ** Licensed under the Apache License, Version 2.0 (the "License");   
    ** you may not use this file except in compliance with the License.   
    ** You may obtain a copy of the License at   
    **   
    **     http://www.apache.org/licenses/LICENSE-2.0   
    **   
    ** Unless required by applicable law or agreed to in writing, software   
    ** distributed under the License is distributed on an "AS IS" BASIS,   
    ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   
    ** See the License for the specific language governing permissions and   
    ** limitations under the License.   
    */   
       
    #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; // SUPPRESS   
        }   
        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
  2. ",   
                    chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));   
       
            // release the chars!   
            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 */   
    }   
       
    /*   
    * In class android.util.Log:   
    *  public static native int println_native(int buffer, int priority, String tag, String msg)   
    */   
    static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,   
            jint bufID, jint priority, jstring tagObj, jstring msgObj)   
    {   
        const char* tag = NULL;   
        const char* msg = NULL;   
       
        if (msgObj == NULL) {   
            jclass npeClazz;   
       
            npeClazz = env->FindClass("java/lang/NullPointerException");   
            assert(npeClazz != NULL);   
       
            env->ThrowNew(npeClazz, "println needs a message");   
            return -1;   
        }   
       
        if (bufID < 0 || bufID >= LOG_ID_MAX) {   
            jclass npeClazz;   
       
            npeClazz = env->FindClass("java/lang/NullPointerException");   
            assert(npeClazz != NULL);   
       
            env->ThrowNew(npeClazz, "bad bufID");   
            return -1;   
        }   
       
        if (tagObj != NULL)   
            tag = env->GetStringUTFChars(tagObj, NULL);   
        msg = env->GetStringUTFChars(msgObj, NULL);   
       
        int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);   
       
        if (tag != NULL)   
            env->ReleaseStringUTFChars(tagObj, tag);   
        env->ReleaseStringUTFChars(msgObj, msg);   
       
        return res;   
    }   
       
    /*   
    * JNI registration.   
    */   
    static JNINativeMethod gMethods[] = {   
        /* name, signature, funcPtr */   
        { "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },   
        { "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },   
    };   
       
    int register_android_util_Log(JNIEnv* env)   
    {   
        jclass clazz = env->FindClass("android/util/Log");   
       
        if (clazz == NULL) {   
            LOGE("Cant find android/util/Log");   
            return -1;   
        }   
          
        levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));   
        levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));   
        levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));   
        levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));   
        levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));   
        levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));   
                      
        return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));   
    }   
       
    }; // namespace android
復(fù)制代碼
在gMethods變量中,定義了println_native本地方法對(duì)應(yīng)的函數(shù)調(diào)用是android_util_Log_println_native。在android_util_Log_println_native函數(shù)中,通過(guò)了各項(xiàng)參數(shù)驗(yàn)證正確后,就調(diào)用運(yùn)行時(shí)庫(kù)函數(shù)__android_log_buf_write來(lái)實(shí)現(xiàn)Log的寫(xiě)入操作。__android_log_buf_write函實(shí)實(shí)現(xiàn)在liblog庫(kù)中,它有4個(gè)參數(shù),分別緩沖區(qū)ID、優(yōu)先級(jí)別ID、Tag字符串和Msg字符串。下面運(yùn)行時(shí)庫(kù)liblog中的__android_log_buf_write的實(shí)現(xiàn)。   
       三. 系統(tǒng)運(yùn)行庫(kù)層日志系統(tǒng)的實(shí)現(xiàn)。   
   
       在系統(tǒng)運(yùn)行庫(kù)層liblog庫(kù)的實(shí)現(xiàn)中,內(nèi)容比較多,這里,我們只關(guān)注日志寫(xiě)入操作__android_log_buf_write的相關(guān)實(shí)現(xiàn):   
  1. int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)   
    {   
        struct iovec vec[3];   
       
        if (!tag)   
            tag = "";   
       
        /* XXX: This needs to go! */   
        if (!strcmp(tag, "HTC_RIL") ||   
            !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */   
            !strcmp(tag, "AT") ||   
            !strcmp(tag, "GSM") ||   
            !strcmp(tag, "STK") ||   
            !strcmp(tag, "CDMA") ||   
            !strcmp(tag, "PHONE") ||   
            !strcmp(tag, "SMS"))   
                bufID = LOG_ID_RADIO;   
       
        vec[0].iov_base   = (unsigned char *) &prio;   
        vec[0].iov_len    = 1;   
        vec[1].iov_base   = (void *) tag;   
        vec[1].iov_len    = strlen(tag) + 1;   
        vec[2].iov_base   = (void *) msg;   
        vec[2].iov_len    = strlen(msg) + 1;   
       
        return write_to_log(bufID, vec, 3);   
    }
復(fù)制代碼
函數(shù)首先是檢查傳進(jìn)來(lái)的tag參數(shù)是否是為HTC_RIL、RIL、AT、GSM、STK、CDMA、PHONE和SMS中的一個(gè),如果是,就無(wú)條件地使用ID為L(zhǎng)OG_ID_RADIO的日志緩沖區(qū)作為寫(xiě)入緩沖區(qū),接著,把傳進(jìn)來(lái)的參數(shù)prio、tag和msg分別存放在一個(gè)向量數(shù)組中,調(diào)用write_to_log函數(shù)來(lái)進(jìn)入下一步操作。write_to_log是一個(gè)函數(shù)指針,定義在文件開(kāi)始的位置上:   
  1. static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);   
    static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
復(fù)制代碼
并且初始化為_(kāi)_write_to_log_init函數(shù):   
  1. static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)   
    {   
    #ifdef HAVE_PTHREADS   
        pthread_mutex_lock(&log_init_lock);   
    #endif   
       
        if (write_to_log == __write_to_log_init) {   
            log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);   
            log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);   
            log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);   
            log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);   
       
            write_to_log = __write_to_log_kernel;   
       
            if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||   
                    log_fds[LOG_ID_EVENTS] < 0) {   
                log_close(log_fds[LOG_ID_MAIN]);   
                log_close(log_fds[LOG_ID_RADIO]);   
                log_close(log_fds[LOG_ID_EVENTS]);   
                log_fds[LOG_ID_MAIN] = -1;   
                log_fds[LOG_ID_RADIO] = -1;   
                log_fds[LOG_ID_EVENTS] = -1;   
                write_to_log = __write_to_log_null;   
            }   
       
            if (log_fds[LOG_ID_SYSTEM] < 0) {   
                log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];   
            }   
        }   
       
    #ifdef HAVE_PTHREADS   
        pthread_mutex_unlock(&log_init_lock);   
    #endif   
       
        return write_to_log(log_id, vec, nr);   
    }
復(fù)制代碼
這里我們可以看到,如果是第一次調(diào)write_to_log函數(shù),write_to_log == __write_to_log_init判斷語(yǔ)句就會(huì)true,于是執(zhí)行l(wèi)og_open函數(shù)打開(kāi)設(shè)備文件,并把文件描述符保存在log_fds數(shù)組中。如果打開(kāi)/dev/LOGGER_LOG_SYSTEM文件失敗,即log_fds[LOG_ID_SYSTEM] < 0,就把log_fds[LOG_ID_SYSTEM]設(shè)置為log_fds[LOG_ID_MAIN],這就是我們上面描述的如果不存在ID為L(zhǎng)OG_ID_SYSTEM的日志緩沖區(qū),就把LOG_ID_SYSTEM設(shè)置為和LOG_ID_MAIN對(duì)應(yīng)的日志緩沖區(qū)了。LOGGER_LOG_MAIN、LOGGER_LOG_RADIO、LOGGER_LOG_EVENTS和LOGGER_LOG_SYSTEM四個(gè)宏定義在system/core/include/cutils/logger.h文件中:   
  1. #define LOGGER_LOG_MAIN                "log/main"   
    #define LOGGER_LOG_RADIO        "log/radio"   
    #define LOGGER_LOG_EVENTS        "log/events"   
    #define LOGGER_LOG_SYSTEM        "log/system"
復(fù)制代碼
接著,把write_to_log函數(shù)指針指向__write_to_log_kernel函數(shù):   
  1. static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)   
    {   
        ssize_t ret;   
        int log_fd;   
       
        if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {   
            log_fd = log_fds[(int)log_id];   
        } else {   
            return EBADF;   
        }   
       
        do {   
            ret = log_writev(log_fd, vec, nr);   
        } while (ret < 0 && errno == EINTR);   
       
        return ret;   
    }
復(fù)制代碼
函數(shù)調(diào)用log_writev來(lái)實(shí)現(xiàn)Log的寫(xiě)入,注意,這里通過(guò)一個(gè)循環(huán)來(lái)寫(xiě)入Log,直到寫(xiě)入成功為止。這里log_writev是一個(gè)宏,在文件開(kāi)始的地方定義為:   
  1. #if FAKE_LOG_DEVICE   
    // This will be defined when building for the host.   
    #define log_open(pathname, flags) fakeLogOpen(pathname, flags)   
    #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)   
    #define log_close(filedes) fakeLogClose(filedes)   
    #else   
    #define log_open(pathname, flags) open(pathname, flags)   
    #define log_writev(filedes, vector, count) writev(filedes, vector, count)   
    #define log_close(filedes) close(filedes)   
    #endif
復(fù)制代碼
這里,我們看到,一般情況下,log_writev就是writev了,這是個(gè)常見(jiàn)的批量文件寫(xiě)入函數(shù),就不多說(shuō)了。   
       至些,整個(gè)調(diào)用過(guò)程就結(jié)束了??偨Y(jié)一下,首先是從應(yīng)用程序?qū)诱{(diào)用應(yīng)用程序框架層的Java接口,應(yīng)用程序框架層的Java接口通過(guò)調(diào)用本層的JNI方法進(jìn)入到系統(tǒng)運(yùn)行庫(kù)層的C接口,系統(tǒng)運(yùn)行庫(kù)層的C接口通過(guò)設(shè)備文件來(lái)訪問(wèn)內(nèi)核空間層的Logger驅(qū)動(dòng)程序。這是一個(gè)典型的調(diào)用過(guò)程,很好地詮釋Android的系統(tǒng)架構(gòu),希望讀者好好領(lǐng)會(huì)

上一篇:Android 軟件開(kāi)發(fā)之?dāng)?shù)據(jù)的 新建 儲(chǔ)存 讀取 刪除 詳解(十四)
下一篇:在Ubuntu上下載、編譯和安裝Android最新源代碼

本版積分規(guī)則

Archiver|新帖|標(biāo)簽|軟件|Sitemap|ZNDS智能電視網(wǎng) ( 蘇ICP備2023012627號(hào) )

網(wǎng)絡(luò)信息服務(wù)信用承諾書(shū) | 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證:蘇B2-20221768 丨 蘇公網(wǎng)安備 32011402011373號(hào)

GMT+8, 2024-10-30 12:22 , Processed in 0.067597 second(s), 15 queries , Redis On.

Powered by Discuz!

監(jiān)督舉報(bào):report#znds.com (請(qǐng)將#替換為@)

© 2007-2024 ZNDS.Com

快速回復(fù) 返回頂部 返回列表