在开发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接口:

[java] view plain copy
  1. ................................................
  2. publicfinalclassLog{
  3. ................................................
  4. /**
  5. *Priorityconstantfortheprintlnmethod;useLog.v.
  6. */
  7. publicstaticfinalintVERBOSE=2;
  8. /**
  9. *Priorityconstantfortheprintlnmethod;useLog.d.
  10. */
  11. publicstaticfinalintDEBUG=3;
  12. /**
  13. *Priorityconstantfortheprintlnmethod;useLog.i.
  14. */
  15. publicstaticfinalintINFO=4;
  16. /**
  17. *Priorityconstantfortheprintlnmethod;useLog.w.
  18. */
  19. publicstaticfinalintWARN=5;
  20. /**
  21. *Priorityconstantfortheprintlnmethod;useLog.e.
  22. */
  23. publicstaticfinalintERROR=6;
  24. /**
  25. *Priorityconstantfortheprintlnmethod.
  26. */
  27. publicstaticfinalintASSERT=7;
  28. .....................................................
  29. publicstaticintv(Stringtag,Stringmsg){
  30. returnprintln_native(LOG_ID_MAIN,VERBOSE,tag,msg);
  31. }
  32. publicstaticintv(Stringtag,Stringmsg,Throwabletr){
  33. returnprintln_native(LOG_ID_MAIN,VERBOSE,tag,msg+'\n'+getStackTraceString(tr));
  34. }
  35. publicstaticintd(Stringtag,Stringmsg){
  36. returnprintln_native(LOG_ID_MAIN,DEBUG,tag,msg);
  37. }
  38. publicstaticintd(Stringtag,Stringmsg,Throwabletr){
  39. returnprintln_native(LOG_ID_MAIN,DEBUG,tag,msg+'\n'+getStackTraceString(tr));
  40. }
  41. publicstaticinti(Stringtag,Stringmsg){
  42. returnprintln_native(LOG_ID_MAIN,INFO,tag,msg);
  43. }
  44. publicstaticinti(Stringtag,Stringmsg,Throwabletr){
  45. returnprintln_native(LOG_ID_MAIN,INFO,tag,msg+'\n'+getStackTraceString(tr));
  46. }
  47. publicstaticintw(Stringtag,Stringmsg){
  48. returnprintln_native(LOG_ID_MAIN,WARN,tag,msg);
  49. }
  50. publicstaticintw(Stringtag,Stringmsg,Throwabletr){
  51. returnprintln_native(LOG_ID_MAIN,WARN,tag,msg+'\n'+getStackTraceString(tr));
  52. }
  53. publicstaticintw(Stringtag,Throwabletr){
  54. returnprintln_native(LOG_ID_MAIN,WARN,tag,getStackTraceString(tr));
  55. }
  56. publicstaticinte(Stringtag,Stringmsg){
  57. returnprintln_native(LOG_ID_MAIN,ERROR,tag,msg);
  58. }
  59. publicstaticinte(Stringtag,Stringmsg,Throwabletr){
  60. returnprintln_native(LOG_ID_MAIN,ERROR,tag,msg+'\n'+getStackTraceString(tr));
  61. }
  62. ..................................................................
  63. /**@hide*/publicstaticnativeintLOG_ID_MAIN=0;
  64. /**@hide*/publicstaticnativeintLOG_ID_RADIO=1;
  65. /**@hide*/publicstaticnativeintLOG_ID_EVENTS=2;
  66. /**@hide*/publicstaticnativeintLOG_ID_SYSTEM=3;
  67. /**@hide*/publicstaticnativeintprintln_native(intbufID,
  68. intpriority,Stringtag,Stringmsg);
  69. }
定义了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:

[cpp] view plain copy
  1. /*//device/libs/android_runtime/android_util_Log.cpp
  2. **
  3. **Copyright2006,TheAndroidOpenSourceProject
  4. **
  5. **LicensedundertheApacheLicense,Version2.0(the"License");
  6. **youmaynotusethisfileexceptincompliancewiththeLicense.
  7. **YoumayobtainacopyoftheLicenseat
  8. **
  9. **http://www.apache.org/licenses/LICENSE-2.0
  10. **
  11. **Unlessrequiredbyapplicablelaworagreedtoinwriting,software
  12. **distributedundertheLicenseisdistributedonan"ASIS"BASIS,
  13. **WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
  14. **SeetheLicenseforthespecificlanguagegoverningpermissionsand
  15. **limitationsundertheLicense.
  16. */
  17. #defineLOG_NAMESPACE"log.tag."
  18. #defineLOG_TAG"Log_println"
  19. #include<assert.h>
  20. #include<cutils/properties.h>
  21. #include<utils/Log.h>
  22. #include<utils/String8.h>
  23. #include"jni.h"
  24. #include"utils/misc.h"
  25. #include"android_runtime/AndroidRuntime.h"
  26. #defineMIN(a,b)((a<b)?a:b)
  27. namespaceandroid{
  28. structlevels_t{
  29. jintverbose;
  30. jintdebug;
  31. jintinfo;
  32. jintwarn;
  33. jinterror;
  34. jintassert;
  35. };
  36. staticlevels_tlevels;
  37. staticinttoLevel(constchar*value)
  38. {
  39. switch(value[0]){
  40. case'V':returnlevels.verbose;
  41. case'D':returnlevels.debug;
  42. case'I':returnlevels.info;
  43. case'W':returnlevels.warn;
  44. case'E':returnlevels.error;
  45. case'A':returnlevels.assert;
  46. case'S':return-1;//SUPPRESS
  47. }
  48. returnlevels.info;
  49. }
  50. staticjbooleanandroid_util_Log_isLoggable(JNIEnv*env,jobjectclazz,jstringtag,jintlevel)
  51. {
  52. #ifndefHAVE_ANDROID_OS
  53. returnfalse;
  54. #else/*HAVE_ANDROID_OS*/
  55. intlen;
  56. charkey[PROPERTY_KEY_MAX];
  57. charbuf[PROPERTY_VALUE_MAX];
  58. if(tag==NULL){
  59. returnfalse;
  60. }
  61. jbooleanresult=false;
  62. constchar*chars=env->GetStringUTFChars(tag,NULL);
  63. if((strlen(chars)+sizeof(LOG_NAMESPACE))>PROPERTY_KEY_MAX){
  64. jclassclazz=env->FindClass("java/lang/IllegalArgumentException");
  65. charbuf2[200];
  66. snprintf(buf2,sizeof(buf2),"Logtag\"%s\"exceedslimitof%dcharacters\n",
  67. chars,PROPERTY_KEY_MAX-sizeof(LOG_NAMESPACE));
  68. //releasethechars!
  69. env->ReleaseStringUTFChars(tag,chars);
  70. env->ThrowNew(clazz,buf2);
  71. returnfalse;
  72. }else{
  73. strncpy(key,LOG_NAMESPACE,sizeof(LOG_NAMESPACE)-1);
  74. strcpy(key+sizeof(LOG_NAMESPACE)-1,chars);
  75. }
  76. env->ReleaseStringUTFChars(tag,chars);
  77. len=property_get(key,buf,"");
  78. intlogLevel=toLevel(buf);
  79. return(logLevel>=0&&level>=logLevel)?true:false;
  80. #endif/*HAVE_ANDROID_OS*/
  81. }
  82. /*
  83. *Inclassandroid.util.Log:
  84. *publicstaticnativeintprintln_native(intbuffer,intpriority,Stringtag,Stringmsg)
  85. */
  86. staticjintandroid_util_Log_println_native(JNIEnv*env,jobjectclazz,
  87. jintbufID,jintpriority,jstringtagObj,jstringmsgObj)
  88. {
  89. constchar*tag=NULL;
  90. constchar*msg=NULL;
  91. if(msgObj==NULL){
  92. jclassnpeClazz;
  93. npeClazz=env->FindClass("java/lang/NullPointerException");
  94. assert(npeClazz!=NULL);
  95. env->ThrowNew(npeClazz,"printlnneedsamessage");
  96. return-1;
  97. }
  98. if(bufID<0||bufID>=LOG_ID_MAX){
  99. jclassnpeClazz;
  100. npeClazz=env->FindClass("java/lang/NullPointerException");
  101. assert(npeClazz!=NULL);
  102. env->ThrowNew(npeClazz,"badbufID");
  103. return-1;
  104. }
  105. if(tagObj!=NULL)
  106. tag=env->GetStringUTFChars(tagObj,NULL);
  107. msg=env->GetStringUTFChars(msgObj,NULL);
  108. intres=__android_log_buf_write(bufID,(android_LogPriority)priority,tag,msg);
  109. if(tag!=NULL)
  110. env->ReleaseStringUTFChars(tagObj,tag);
  111. env->ReleaseStringUTFChars(msgObj,msg);
  112. returnres;
  113. }
  114. /*
  115. *JNIregistration.
  116. */
  117. staticJNINativeMethodgMethods[]={
  118. /*name,signature,funcPtr*/
  119. {"isLoggable","(Ljava/lang/String;I)Z",(void*)android_util_Log_isLoggable},
  120. {"println_native","(IILjava/lang/String;Ljava/lang/String;)I",(void*)android_util_Log_println_native},
  121. };
  122. intregister_android_util_Log(JNIEnv*env)
  123. {
  124. jclassclazz=env->FindClass("android/util/Log");
  125. if(clazz==NULL){
  126. LOGE("Can'tfindandroid/util/Log");
  127. return-1;
  128. }
  129. levels.verbose=env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz,"VERBOSE","I"));
  130. levels.debug=env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz,"DEBUG","I"));
  131. levels.info=env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz,"INFO","I"));
  132. levels.warn=env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz,"WARN","I"));
  133. levels.error=env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz,"ERROR","I"));
  134. levels.assert=env->GetStaticIntField(clazz,env->GetStaticFieldID(clazz,"ASSERT","I"));
  135. returnAndroidRuntime::registerNativeMethods(env,"android/util/Log",gMethods,NELEM(gMethods));
  136. }
  137. };//namespaceandroid
在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的相关实现:

[cpp] view plain copy
  1. int__android_log_buf_write(intbufID,intprio,constchar*tag,constchar*msg)
  2. {
  3. structiovecvec[3];
  4. if(!tag)
  5. tag="";
  6. /*XXX:Thisneedstogo!*/
  7. if(!strcmp(tag,"HTC_RIL")||
  8. !strncmp(tag,"RIL",3)||/*Anylogtagwith"RIL"astheprefix*/
  9. !strcmp(tag,"AT")||
  10. !strcmp(tag,"GSM")||
  11. !strcmp(tag,"STK")||
  12. !strcmp(tag,"CDMA")||
  13. !strcmp(tag,"PHONE")||
  14. !strcmp(tag,"SMS"))
  15. bufID=LOG_ID_RADIO;
  16. vec[0].iov_base=(unsignedchar*)&prio;
  17. vec[0].iov_len=1;
  18. vec[1].iov_base=(void*)tag;
  19. vec[1].iov_len=strlen(tag)+1;
  20. vec[2].iov_base=(void*)msg;
  21. vec[2].iov_len=strlen(msg)+1;
  22. returnwrite_to_log(bufID,vec,3);
  23. }

函数首先是检查传进来的tag参数是否是为HTC_RIL、RIL、AT、GSM、STK、CDMA、PHONE和SMS中的一个,如果是,就无条件地使用ID为LOG_ID_RADIO的日志缓冲区作为写入缓冲区,接着,把传进来的参数prio、tag和msg分别存放在一个向量数组中,调用write_to_log函数来进入下一步操作。write_to_log是一个函数指针,定义在文件开始的位置上:

[cpp] view plain copy
  1. staticint__write_to_log_init(log_id_t,structiovec*vec,size_tnr);
  2. staticint(*write_to_log)(log_id_t,structiovec*vec,size_tnr)=__write_to_log_init;
并且初始化为__write_to_log_init函数:

[cpp] view plain copy
  1. staticint__write_to_log_init(log_id_tlog_id,structiovec*vec,size_tnr)
  2. {
  3. #ifdefHAVE_PTHREADS
  4. pthread_mutex_lock(&log_init_lock);
  5. #endif
  6. if(write_to_log==__write_to_log_init){
  7. log_fds[LOG_ID_MAIN]=log_open("/dev/"LOGGER_LOG_MAIN,O_WRONLY);
  8. log_fds[LOG_ID_RADIO]=log_open("/dev/"LOGGER_LOG_RADIO,O_WRONLY);
  9. log_fds[LOG_ID_EVENTS]=log_open("/dev/"LOGGER_LOG_EVENTS,O_WRONLY);
  10. log_fds[LOG_ID_SYSTEM]=log_open("/dev/"LOGGER_LOG_SYSTEM,O_WRONLY);
  11. write_to_log=__write_to_log_kernel;
  12. if(log_fds[LOG_ID_MAIN]<0||log_fds[LOG_ID_RADIO]<0||
  13. log_fds[LOG_ID_EVENTS]<0){
  14. log_close(log_fds[LOG_ID_MAIN]);
  15. log_close(log_fds[LOG_ID_RADIO]);
  16. log_close(log_fds[LOG_ID_EVENTS]);
  17. log_fds[LOG_ID_MAIN]=-1;
  18. log_fds[LOG_ID_RADIO]=-1;
  19. log_fds[LOG_ID_EVENTS]=-1;
  20. write_to_log=__write_to_log_null;
  21. }
  22. if(log_fds[LOG_ID_SYSTEM]<0){
  23. log_fds[LOG_ID_SYSTEM]=log_fds[LOG_ID_MAIN];
  24. }
  25. }
  26. #ifdefHAVE_PTHREADS
  27. pthread_mutex_unlock(&log_init_lock);
  28. #endif
  29. returnwrite_to_log(log_id,vec,nr);
  30. }
这里我们可以看到,如果是第一次调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文件中:

[cpp] view plain copy
  1. #defineLOGGER_LOG_MAIN"log/main"
  2. #defineLOGGER_LOG_RADIO"log/radio"
  3. #defineLOGGER_LOG_EVENTS"log/events"
  4. #defineLOGGER_LOG_SYSTEM"log/system"
接着,把write_to_log函数指针指向__write_to_log_kernel函数:

[cpp] view plain copy
  1. staticint__write_to_log_kernel(log_id_tlog_id,structiovec*vec,size_tnr)
  2. {
  3. ssize_tret;
  4. intlog_fd;
  5. if(/*(int)log_id>=0&&*/(int)log_id<(int)LOG_ID_MAX){
  6. log_fd=log_fds[(int)log_id];
  7. }else{
  8. returnEBADF;
  9. }
  10. do{
  11. ret=log_writev(log_fd,vec,nr);
  12. }while(ret<0&&errno==EINTR);
  13. returnret;
  14. }
函数调用log_writev来实现Log的写入,注意,这里通过一个循环来写入Log,直到写入成功为止。这里log_writev是一个宏,在文件开始的地方定义为:

[cpp] view plain copy
  1. #ifFAKE_LOG_DEVICE
  2. //Thiswillbedefinedwhenbuildingforthehost.
  3. #definelog_open(pathname,flags)fakeLogOpen(pathname,flags)
  4. #definelog_writev(filedes,vector,count)fakeLogWritev(filedes,vector,count)
  5. #definelog_close(filedes)fakeLogClose(filedes)
  6. #else
  7. #definelog_open(pathname,flags)open(pathname,flags)
  8. #definelog_writev(filedes,vector,count)writev(filedes,vector,count)
  9. #definelog_close(filedes)close(filedes)
  10. #endif
这里,我们看到,一般情况下,log_writev就是writev了,这是个常见的批量文件写入函数,就不多说了。

至些,整个调用过程就结束了。总结一下,首先是从应用程序层调用应用程序框架层的Java接口,应用程序框架层的Java接口通过调用本层的JNI方法进入到系统运行库层的C接口,系统运行库层的C接口通过设备文件来访问内核空间层的Logger驱动程序。这是一个典型的调用过程,很好地诠释Android的系统架构,希望读者好好领会。




http://blog.csdn.net/luoshengyang/article/details/6598703

更多相关文章

  1. Android之弹出/隐藏系统软键盘
  2. android 判断网络是否可用,并调用系统设置项
  3. android 调用系统相机程序,存放文件夹创建不了(miui 2.3.9系统)
  4. 32位机器Ubuntu系统编译android Froyo注意修改点
  5. 给android添加系统属性
  6. android adb 读写模式 挂载文件系统
  7. windows系统 android源码下载
  8. android 修改系统显示u盘的名称
  9. android调用系统(相机)的图片,并且返回

随机推荐

  1. 在Access中利用Jquery技术实现专业的界面
  2. 如何使用特定样式获取jquery中的第一个和
  3. jQuery插件制作备忘
  4. Jquery插件Thickbox的使用总结及自定义设
  5. 如何在更新面板刷新之后运行一些javascri
  6. 新手怎么学习jQuery快成
  7. 优秀js开源框架-jQuery使用手册(6)
  8. jquery常用代码片段
  9. JQuery的一些基本功能代码(CH1&2)
  10. 【jQuery】调用delay()方法延时执行动画