0 引言
在编写Android App的过程中,使用最多的打印函数是Log.d(tag,msg),今天我们来分析一下这个函数打印的过程。打印过程中,最重要的是打印数据的流向分析,下面这张图总结了打印过程中数据的变化和流向:(本博客的分析过程基于android13_r16版本。)
图片的左边是数据,右边是对应调用的函数,这张图可以很好的概括Log.d打印的整个过程。从Java层,到JNI层,再到Native层,最后通过socket来完成数据的发送。
1 过程分析
我们随便找到一个打印日志的地方,可以看到这样一个函数Log.d(tag, msg),只需要我们输入日志的tag和日志的具体信息msg,就可以完成打印的过程,进入这个函数可以看到,内部调用了一个native函数println_native,并且在这一层加入了buffer_id-LOG_ID_MAIN,以及优先级priority-DEBUG。
找到这个函数的具体实现JNI函数android_util_Log_println_native,这个函数重点的部分就一个地方,那就是调用了__android_log_buf_write函数。这里的JNI层主要就是获取到Java层的tag和msg,其他事情基本没干。
进入到native层,可以看到代码中将前面的函数参数组装成了一个结构体__android_log_message,并且调用了下一个函数__android_log_write_log_message。
在__android_log_write_log_message函数中,对buffer_id、tag和优先级priority进行了检验,然后继续调用了logger_function函数。
这个函数实际上就是__android_log_logd_logger函数,进入这个函数看看里面做了哪些事情。
在__android_log_logd_logger函数函数中,又将数据封装成为了一个新的结构体iovec,并且把数据放在了一个iovec类型的数组中,接着调用write_to_log函数。
write_to_log函数中做的比较重要的事情是获取到了打印时间,用结构体timespec来记录,然后调用了LogdWrite函数将iovec数组和timespec一起发送出去。
进入到LogdWrite后,做了比较重要的几件事情就是:一是拿到一个socket叫做LogdSocket;二是根据时间戳信息和线程信息组成一个新的数据结构android_log_header_t,三是把header放入到iovec数据结构中,加上之前的iovec[3]数组,组装成newVec[4]数据;四是通过writev函数写入到socket中。
至此,整个Log.d(tag,msg)的过程就分析完成了,可以根据代码和文章开头的图片总结再捋一遍。