Android应用程序框架层和系统运行库层日志系统源代码分析
在开发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
- ................................................
- publicfinalclassLog{
- ................................................
- /**
- *Priorityconstantfortheprintlnmethod;useLog.v.
- */
- publicstaticfinalintVERBOSE=2;
- /**
- *Priorityconstantfortheprintlnmethod;useLog.d.
- */
- publicstaticfinalintDEBUG=3;
- /**
- *Priorityconstantfortheprintlnmethod;useLog.i.
- */
- publicstaticfinalintINFO=4;
- /**
- *Priorityconstantfortheprintlnmethod;useLog.w.
- */
- publicstaticfinalintWARN=5;
- /**
- *Priorityconstantfortheprintlnmethod;useLog.e.
- */
- publicstaticfinalintERROR=6;
- /**
- *Priorityconstantfortheprintlnmethod.
- */
- publicstaticfinalintASSERT=7;
- .....................................................
- publicstaticintv(Stringtag,Stringmsg){
- returnprintln_native(LOG_ID_MAIN,VERBOSE,tag,msg);
- }
- publicstaticintv(Stringtag,Stringmsg,Throwabletr){
- returnprintln_native(LOG_ID_MAIN,VERBOSE,tag,msg+'\n'+getStackTraceString(tr));
- }
- publicstaticintd(Stringtag,Stringmsg){
- returnprintln_native(LOG_ID_MAIN,DEBUG,tag,msg);
- }
- publicstaticintd(Stringtag,Stringmsg,Throwabletr){
- returnprintln_native(LOG_ID_MAIN,DEBUG,tag,msg+'\n'+getStackTraceString(tr));
- }
- publicstaticinti(Stringtag,Stringmsg){
- returnprintln_native(LOG_ID_MAIN,INFO,tag,msg);
- }
- publicstaticinti(Stringtag,Stringmsg,Throwabletr){
- returnprintln_native(LOG_ID_MAIN,INFO,tag,msg+'\n'+getStackTraceString(tr));
- }
- publicstaticintw(Stringtag,Stringmsg){
- returnprintln_native(LOG_ID_MAIN,WARN,tag,msg);
- }
- publicstaticintw(Stringtag,Stringmsg,Throwabletr){
- returnprintln_native(LOG_ID_MAIN,WARN,tag,msg+'\n'+getStackTraceString(tr));
- }
- publicstaticintw(Stringtag,Throwabletr){
- returnprintln_native(LOG_ID_MAIN,WARN,tag,getStackTraceString(tr));
- }
- publicstaticinte(Stringtag,Stringmsg){
- returnprintln_native(LOG_ID_MAIN,ERROR,tag,msg);
- }
- publicstaticinte(Stringtag,Stringmsg,Throwabletr){
- returnprintln_native(LOG_ID_MAIN,ERROR,tag,msg+'\n'+getStackTraceString(tr));
- }
- ..................................................................
- /**@hide*/publicstaticnativeintLOG_ID_MAIN=0;
- /**@hide*/publicstaticnativeintLOG_ID_RADIO=1;
- /**@hide*/publicstaticnativeintLOG_ID_EVENTS=2;
- /**@hide*/publicstaticnativeintLOG_ID_SYSTEM=3;
- /**@hide*/publicstaticnativeintprintln_native(intbufID,
- intpriority,Stringtag,Stringmsg);
- }
在整个Log接口中,最关键的地方声明了println_native本地方法,所有的Log接口都是通过调用这个本地方法来实现Log的定入。下面我们就继续分析这个本地方法println_native。
二.应用程序框架层日志系统JNI方法的实现。
在frameworks/base/core/jni/android_util_Log.cpp文件中,实现JNI方法println_native:
[cpp] view plain copy
- /*//device/libs/android_runtime/android_util_Log.cpp
- **
- **Copyright2006,TheAndroidOpenSourceProject
- **
- **LicensedundertheApacheLicense,Version2.0(the"License");
- **youmaynotusethisfileexceptincompliancewiththeLicense.
- **YoumayobtainacopyoftheLicenseat
- **
- **http://www.apache.org/licenses/LICENSE-2.0
- **
- **Unlessrequiredbyapplicablelaworagreedtoinwriting,software
- **distributedundertheLicenseisdistributedonan"ASIS"BASIS,
- **WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
- **SeetheLicenseforthespecificlanguagegoverningpermissionsand
- **limitationsundertheLicense.
- */
- #defineLOG_NAMESPACE"log.tag."
- #defineLOG_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"
- #defineMIN(a,b)((a<b)?a:b)
- namespaceandroid{
- structlevels_t{
- jintverbose;
- jintdebug;
- jintinfo;
- jintwarn;
- jinterror;
- jintassert;
- };
- staticlevels_tlevels;
- staticinttoLevel(constchar*value)
- {
- switch(value[0]){
- case'V':returnlevels.verbose;
- case'D':returnlevels.debug;
- case'I':returnlevels.info;
- case'W':returnlevels.warn;
- case'E':returnlevels.error;
- case'A':returnlevels.assert;
- case'S':return-1;//SUPPRESS
- }
- returnlevels.info;
- }
- staticjbooleanandroid_util_Log_isLoggable(JNIEnv*env,jobjectclazz,jstringtag,jintlevel)
- {
- #ifndefHAVE_ANDROID_OS
- returnfalse;
- #else/*HAVE_ANDROID_OS*/
- intlen;
- charkey[PROPERTY_KEY_MAX];
- charbuf[PROPERTY_VALUE_MAX];
- if(tag==NULL){
- returnfalse;
- }
- jbooleanresult=false;
- constchar*chars=env->GetStringUTFChars(tag,NULL);
- if((strlen(chars)+sizeof(LOG_NAMESPACE))>PROPERTY_KEY_MAX){
- jclassclazz=env->FindClass("java/lang/IllegalArgumentException");
- charbuf2[200];
- snprintf(buf2,sizeof(buf2),"Logtag\"%s\"exceedslimitof%dcharacters\n",
- chars,PROPERTY_KEY_MAX-sizeof(LOG_NAMESPACE));
- //releasethechars!
- env->ReleaseStringUTFChars(tag,chars);
- env->ThrowNew(clazz,buf2);
- returnfalse;
- }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,"");
- intlogLevel=toLevel(buf);
- return(logLevel>=0&&level>=logLevel)?true:false;
- #endif/*HAVE_ANDROID_OS*/
- }
- /*
- *Inclassandroid.util.Log:
- *publicstaticnativeintprintln_native(intbuffer,intpriority,Stringtag,Stringmsg)
- */
- staticjintandroid_util_Log_println_native(JNIEnv*env,jobjectclazz,
- jintbufID,jintpriority,jstringtagObj,jstringmsgObj)
- {
- constchar*tag=NULL;
- constchar*msg=NULL;
- if(msgObj==NULL){
- jclassnpeClazz;
- npeClazz=env->FindClass("java/lang/NullPointerException");
- assert(npeClazz!=NULL);
- env->ThrowNew(npeClazz,"printlnneedsamessage");
- return-1;
- }
- if(bufID<0||bufID>=LOG_ID_MAX){
- jclassnpeClazz;
- npeClazz=env->FindClass("java/lang/NullPointerException");
- assert(npeClazz!=NULL);
- env->ThrowNew(npeClazz,"badbufID");
- return-1;
- }
- if(tagObj!=NULL)
- tag=env->GetStringUTFChars(tagObj,NULL);
- msg=env->GetStringUTFChars(msgObj,NULL);
- intres=__android_log_buf_write(bufID,(android_LogPriority)priority,tag,msg);
- if(tag!=NULL)
- env->ReleaseStringUTFChars(tagObj,tag);
- env->ReleaseStringUTFChars(msgObj,msg);
- returnres;
- }
- /*
- *JNIregistration.
- */
- staticJNINativeMethodgMethods[]={
- /*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},
- };
- intregister_android_util_Log(JNIEnv*env)
- {
- jclassclazz=env->FindClass("android/util/Log");
- if(clazz==NULL){
- LOGE("Can'tfindandroid/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"));
- returnAndroidRuntime::registerNativeMethods(env,"android/util/Log",gMethods,NELEM(gMethods));
- }
- };//namespaceandroid
三.系统运行库层日志系统的实现。
在系统运行库层liblog库的实现中,内容比较多,这里,我们只关注日志写入操作__android_log_buf_write的相关实现:
[cpp] view plain copy
- int__android_log_buf_write(intbufID,intprio,constchar*tag,constchar*msg)
- {
- structiovecvec[3];
- if(!tag)
- tag="";
- /*XXX:Thisneedstogo!*/
- if(!strcmp(tag,"HTC_RIL")||
- !strncmp(tag,"RIL",3)||/*Anylogtagwith"RIL"astheprefix*/
- !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=(unsignedchar*)&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;
- returnwrite_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是一个函数指针,定义在文件开始的位置上:
[cpp] view plain copy
- staticint__write_to_log_init(log_id_t,structiovec*vec,size_tnr);
- staticint(*write_to_log)(log_id_t,structiovec*vec,size_tnr)=__write_to_log_init;
[cpp] view plain copy
- staticint__write_to_log_init(log_id_tlog_id,structiovec*vec,size_tnr)
- {
- #ifdefHAVE_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];
- }
- }
- #ifdefHAVE_PTHREADS
- pthread_mutex_unlock(&log_init_lock);
- #endif
- returnwrite_to_log(log_id,vec,nr);
- }
[cpp] view plain copy
- #defineLOGGER_LOG_MAIN"log/main"
- #defineLOGGER_LOG_RADIO"log/radio"
- #defineLOGGER_LOG_EVENTS"log/events"
- #defineLOGGER_LOG_SYSTEM"log/system"
[cpp] view plain copy
- staticint__write_to_log_kernel(log_id_tlog_id,structiovec*vec,size_tnr)
- {
- ssize_tret;
- intlog_fd;
- if(/*(int)log_id>=0&&*/(int)log_id<(int)LOG_ID_MAX){
- log_fd=log_fds[(int)log_id];
- }else{
- returnEBADF;
- }
- do{
- ret=log_writev(log_fd,vec,nr);
- }while(ret<0&&errno==EINTR);
- returnret;
- }
[cpp] view plain copy
- #ifFAKE_LOG_DEVICE
- //Thiswillbedefinedwhenbuildingforthehost.
- #definelog_open(pathname,flags)fakeLogOpen(pathname,flags)
- #definelog_writev(filedes,vector,count)fakeLogWritev(filedes,vector,count)
- #definelog_close(filedes)fakeLogClose(filedes)
- #else
- #definelog_open(pathname,flags)open(pathname,flags)
- #definelog_writev(filedes,vector,count)writev(filedes,vector,count)
- #definelog_close(filedes)close(filedes)
- #endif
至些,整个调用过程就结束了。总结一下,首先是从应用程序层调用应用程序框架层的Java接口,应用程序框架层的Java接口通过调用本层的JNI方法进入到系统运行库层的C接口,系统运行库层的C接口通过设备文件来访问内核空间层的Logger驱动程序。这是一个典型的调用过程,很好地诠释Android的系统架构,希望读者好好领会。
http://blog.csdn.net/luoshengyang/article/details/6598703
更多相关文章
- Android之弹出/隐藏系统软键盘
- android 判断网络是否可用,并调用系统设置项
- android 调用系统相机程序,存放文件夹创建不了(miui 2.3.9系统)
- 32位机器Ubuntu系统编译android Froyo注意修改点
- 给android添加系统属性
- android adb 读写模式 挂载文件系统
- windows系统 android源码下载
- android 修改系统显示u盘的名称
- android调用系统(相机)的图片,并且返回