<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                原文出處——>[Android應用程序框架層和系統運行庫層日志系統源代碼分析](http://blog.csdn.net/luoshengyang/article/details/6598703) 在開發Android應用程序時,少不了使用Log來監控和調試程序的執行。在上一篇文章Android日志系統驅動程序Logger源代碼分析中,我們分析了驅動程序Logger的源代碼,在前面的文章淺談Android系統開發中Log的使用一文,我們也簡單介紹在應用程序中使Log的方法,在這篇文章中,我們將詳細介紹Android應用程序框架層和系統運行庫存層日志系統的源代碼,使得我們可以更好地理解Android的日志系統的實現。 我們在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 { ................................................ /** * 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 + '\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)); } .................................................................. /** @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); } ~~~ 定義了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: ~~~ /* //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\n", 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("Can't 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 ~~~ 在gMethods變量中,定義了println_native本地方法對應的函數調用是android_util_Log_println_native。在android_util_Log_println_native函數中,通過了各項參數驗證正確后,就調用運行時庫函數__android_log_buf_write來實現Log的寫入操作。__android_log_buf_write函實實現在liblog庫中,它有4個參數,分別緩沖區ID、優先級別ID、Tag字符串和Msg字符串。下面運行時庫liblog中的__android_log_buf_write的實現。 **三. 系統運行庫層日志系統的實現。** 在系統運行庫層liblog庫的實現中,內容比較多,這里,我們只關注日志寫入操作__android_log_buf_write的相關實現: ~~~ 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); } ~~~ 函數首先是檢查傳進來的tag參數是否是為HTC_RIL、RIL、AT、GSM、STK、CDMA、PHONE和SMS中的一個,如果是,就無條件地使用ID為LOG_ID_RADIO的日志緩沖區作為寫入緩沖區,接著,把傳進來的參數prio、tag和msg分別存放在一個向量數組中,調用write_to_log函數來進入下一步操作。write_to_log是一個函數指針,定義在文件開始的位置上: ~~~ 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; ~~~ 并且初始化為__write_to_log_init函數: ~~~ 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); } ~~~ 這里我們可以看到,如果是第一次調write_to_log函數,write_to_log == __write_to_log_init判斷語句就會true,于是執行log_open函數打開設備文件,并把文件描述符保存在log_fds數組中。如果打開/dev/LOGGER_LOG_SYSTEM文件失敗,即log_fds[LOG_ID_SYSTEM] < 0,就把log_fds[LOG_ID_SYSTEM]設置為log_fds[LOG_ID_MAIN],這就是我們上面描述的如果不存在ID為LOG_ID_SYSTEM的日志緩沖區,就把LOG_ID_SYSTEM設置為和LOG_ID_MAIN對應的日志緩沖區了。LOGGER_LOG_MAIN、LOGGER_LOG_RADIO、LOGGER_LOG_EVENTS和LOGGER_LOG_SYSTEM四個宏定義在system/core/include/cutils/logger.h文件中: ~~~ #define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" #define LOGGER_LOG_SYSTEM "log/system" ~~~ 接著,把write_to_log函數指針指向__write_to_log_kernel函數: ~~~ 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; } ~~~ 函數調用log_writev來實現Log的寫入,注意,這里通過一個循環來寫入Log,直到寫入成功為止。這里log_writev是一個宏,在文件開始的地方定義為: ~~~ #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 ~~~ 這里,我們看到,一般情況下,log_writev就是writev了,這是個常見的批量文件寫入函數,就不多說了。 至些,整個調用過程就結束了。總結一下,首先是從應用程序層調用應用程序框架層的Java接口,應用程序框架層的Java接口通過調用本層的JNI方法進入到系統運行庫層的C接口,系統運行庫層的C接口通過設備文件來訪問內核空間層的Logger驅動程序。這是一個典型的調用過程,很好地詮釋Android的系統架構,希望讀者好好領會。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看