Android提供瞭用戶級輕量的LOG機制,它的實現貫穿瞭Java,JNI,本地c/c++實現以及LINUX內核驅動等Android的各個層次,而且還足夠簡單清晰,是一個相當不錯的解讀案例。本系列文章針對LOG機制的內部實現機理進行解讀,本文是系列之三,解讀android.util.Log的JNI實現,以及在c/c++的本地實現中如何操作設備文件寫Log信息。
類Log的JNI實現
由前文知道,類android.util.Log有兩個Native方法,需要通過JNI在c/c++中實現。
<pre class="java" name="code">public static native boolean isLoggable(String tag, int level);
public static native int println_native(int bufID,
int priority, String tag, String msg);
這兩個方法是在frameworks/base/core/jni/android_util_log.cpp中實現的。如何實現JNI的,在這裡不做表述。不過最終這兩個方法分別轉入瞭下列兩個c/c++函數的調用。
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
isLoggable()的實現
isLoggable的實現是比較<level>(來自參數)與當前property裡設定的“log.tag.<tag>”(<tag>來自參數)的值,大於或等於都是可記錄的。程序實現片斷如下:
// LOG_NAMESPACE : “log.tag.”
// chars: convert from param<tag>
strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);
strcpy(key + sizeof(LOG_NAMESPACE) – 1, chars);
len = property_get(key, buf, "");
int logLevel = toLevel(buf);
return (logLevel >= 0 && level >= logLevel) ? true : false;
println_native()的實現
函數android_util_Log_println_native() [文件android_util.Log.cpp中]調用瞭__android_log_buf_write()[文件system/core/liblog/logd_write.c中]。__android_log_buf_write()組織瞭參數,又調用瞭write_to_log這個函數指針。
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初始是指向__write_to_log_init()這個函數的。所以第一次執行write_to_log的時候是執行瞭__write_to_log_init()。而如果write_to_log不是第一次被執行,它已經在__write_to_log_init()裡被修改指向瞭__write_to_log_kernel()。
先看__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_init()),就打開相應的設備文件,獲取描述符,並把write_to_log指向__write_to_log_kernel()。再在__write_to_log_kernel()中具體執行寫入文件操作。
看__write_to_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;
}
總結一下,println_native()的操作,就是打開設備文件(如果還沒打開),然後寫入數據。而具體怎麼寫入的,要看Log的設備驅動Logger的實現。