当前位置:编程学习 > C/C++ >>

解读Android LOG机制的实现:(3)JNI及c/c++域写设备文件

 

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的实现。

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,