Android源码之Handler(一)
在Android系统中,Handler是一个很重要的概念.可以说,在Android系统中,Handler的身影无处不在。
Handler提供了若干个构造函数,我们就从Handler的构造函数来开始分析Handler系统的实现.Handler的构造函数的实现如下:
[//Handler的构造函数
publicHandler(){
this(null,false);
}
publicHandler(Callbackcallback){
this(callback,false);
}
publicHandler(Looperlooper){
this(looper,null,false);
}
publicHandler(Looperlooper,Callbackcallback){
this(looper,callback,false);
}
publicHandler(booleanasync){
this(null,async);
}
publicHandler(Callbackcallback,booleanasync){
if(FIND_POTENTIAL_LEAKS){
[//FIND_POTENTIAL_LEAKS
privatestaticfinalbooleanFIND_POTENTIAL_LEAKS=false;
FIND_POTENTIAL_LEAKS设置为true的时候,会检测内存泄漏可能性.
]//FIND_POTENTIAL_LEAKS
finalClass<?extendsHandler>klass=getClass();
if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&
(klass.getModifiers()&Modifier.STATIC)==0){
Log.w(TAG,"ThefollowingHandlerclassshouldbestaticorleaksmightoccur:"+
klass.getCanonicalName());
}
[//if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&(klass.getModifiers()&Modifier.STATIC)==0)
检测内存泄漏是通过Java的反射机制来实现的.
]//if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&(klass.getModifiers()&Modifier.STATIC)==0)
}
[//if(FIND_POTENTIAL_LEAKS)
这段代码是为了检测Handler可能的内存泄漏.当Handler被继承定义成匿名函数或者是某一类的内部类的时候,如果不把该Handler定义成static,则可能发生内存泄漏.为什么呢?
因为不是static的内部类会默认持有一个指向父类的指针,而发给这个Handler的Message又会持有这个Handler的指针.这样如果这个Message不销毁的话,则这个Handler是不会被垃圾回收的.
]//if(FIND_POTENTIAL_LEAKS)
mLooper=Looper.myLooper();
[//mLooper=Looper.myLooper()
得到的是当前线程的Looper.那么这个Looper是个什么呢?在这里我们先来看一下Looper的实现。
在Android系统中,Looper是按照如下方式使用的:
classLooperThreadextendsThread{
*publicHandlermHandler;
*
*publicvoidrun(){
*Looper.prepare();
*
*mHandler=newHandler(){
*publicvoidhandleMessage(Messagemsg){
*//processincomingmessageshere
*}
*};
*
*Looper.loop();
*}
*}
由上面的代码可以看出,使用Looper要先调用Looper.prepare函数,然后构造一个Handler对象,最后调用Looper.loop函数。经过上面的过程之后,就可以利用Handler机制来发送和处理消息了。
我们先来看一下prepare的实现:
publicstaticvoidprepare(){
prepare(true);
}
privatestaticvoidprepare(booleanquitAllowed){
if(sThreadLocal.get()!=null){
[//if(sThreadLocal.get()!=null)
这里首先会先判断之前是否已经为线程设置过Looper
sThreadLocal是Looper内部定义的一个static成员变量,定义如下:
staticfinalThreadLocal<Looper>sThreadLocal=newThreadLocal<Looper>();
ThreadLocal的构造函数定义如下:
[//ThreadLocal的构造函数
publicThreadLocal(){}
由此可见,ThreadLocal的构造函数是个空函数,什么也不做。
]//ThreadLocal的构造函数
注意,这里的成员变量sThreadLocal是static的。
我们接下来看一下ThreadLocal的get函数的实现:
publicTget(){
//Optimizedforthefastpath.
ThreadcurrentThread=Thread.currentThread();
[//ThreadcurrentThread=Thread.currentThread()
这里调用Thread的currentThread函数当得到当前线程。
]//ThreadcurrentThread=Thread.currentThread()
Valuesvalues=values(currentThread);
[//Valuesvalues=values(currentThread)
当得到了当前线程的Thread变量之后,会调用values函数。values函数的定义如下:
Valuesvalues(Threadcurrent){
returncurrent.localValues;
}
values函数会直接返回Thread变量的localValues变量,由此可知,其实TheadLocal机制其实是将值保存到每一个Thread变量中去的.
]//Valuesvalues=values(currentThread)
if(values!=null){
[//if(values!=null)
下面这段代码是在当前线程的localValues成员变量中取ThreadLocal保存的值。
]//if(values!=null)
Object[]table=values.table;
intindex=hash&values.mask;
[//intindex=hash&values.mask
这句代码实在计算索引值。mask的值就是HashTable的长度。那么这个hash变量的值是如何计算的呢?
我们看一下hash成员变量是如何初始化的?
privatefinalinthash=hashCounter.getAndAdd(0x61c88647*2);
privatestaticAtomicIntegerhashCounter=newAtomicInteger(0);
我们看到,hash成员变量是final的,并且是非static的。也就是说每一个ThreadLocal对象持有一个hash变量。更进一步,我们可以得到这样的结论:对于某一类的ThreadLocal变量(通俗的讲就是ThreadLocal的模版参数一样),其实维护的是一个hash变量值,也就是说会映射到同一个索引的地方去。
]//intindex=hash&values.mask
if(this.reference==table[index]){
return(T)table[index+1];
}
[//if(this.reference==table[index])
在Values的实现中,table会在index保存ThreadLocal对象,而在index+1的位置保存ThreadLocal的值。为什么要这么设计呢?
我们先来看一下reference的定义:
privatefinalReference<ThreadLocal<T>>reference=newWeakReference<ThreadLocal<T>>(this);
可以看到reference是个弱引用。所以这里要先比较一下reference和table[index].是为了防止内存回收。
]//if(this.reference==table[index])
}else{
values=initializeValues(currentThread);
[//values=initializeValues(currentThread)
ValuesinitializeValues(Threadcurrent){
returncurrent.localValues=newValues();
[//newValues()
Values类是ThreadLocal的一个内部类,其实它的作用就是一个HashTable.我们先来看一下Values的构造函数:
Values(){
initializeTable(INITIAL_SIZE);
[//initializeTable(INITIAL_SIZE)
privatevoidinitializeTable(intcapacity){
this.table=newObject[capacity*2];
this.mask=table.length-1;
this.clean=0;
this.maximumLoad=capacity*2/3;//2/3
}
initializeTable函数就是初始化Values的几个重要的成员变量。
INITIAL_SIZE是Values定义的常量。
privatestaticfinalintINITIAL_SIZE=16;
]//initializeTable(INITIAL_SIZE)
this.size=0;
this.tombstones=0;
}
]//newValues()
}
如果当前线程还没有设置过localValues,则会创建一个Values对象,并赋值给当前线程。
]//values=initializeValues(currentThread)
}
return(T)values.getAfterMiss(this);
[//values.getAfterMiss(this)
当第一个索引位置没有得到值的情况下或者第一次设置Values成员变量,则会调用getAfterMiss来尝试查找有冲突的情况下的值:
ObjectgetAfterMiss(ThreadLocal<?>key){
Object[]table=this.table;
intindex=key.hash&mask;
if(table[index]==null){
[//if(table[index]==null)
如果table的index索引的位置的值是null,则说明没有发生过冲突,也就没有必要在继续查找index之后的位置
]//if(table[index]==null)
Objectvalue=key.initialValue();
[//Objectvalue=key.initialValue();
这里会调用ThreadLocal的initialValue函数来初始化一个值
protectedTinitialValue(){
returnnull;
}
默认的initialValue函数的实现就是返回null
]//Objectvalue=key.initialValue();
if(this.table==table&&table[index]==null){
[//if(this.table==table&&table[index]==null)
这里为什么还要判断一下?是为了防止在initialValue函数中改变了table,如果发现没有改变,则设置
]//if(this.table==table&&table[index]==null)
table[index]=key.reference;
table[index+1]=value;
size++;
cleanUp();
[//cleanUp()
这里还会调用cleanUp函数来清空失效的
privatevoidcleanUp(){
if(rehash()){
[//if(rehash())
这里会先判断是否需要rehash。如果rehash了,则不必往下进行了.rehash函数的定义如下:
privatebooleanrehash(){
if(tombstones+size<maximumLoad){
returnfalse;
}
[//if(tombstones+size<maximumLoad)
tombstones+size表示目前装载的数目
]//if(tombstones+size<maximumLoad)
intcapacity=table.length>>1;
[//intcapacity=table.length>>1
因为在hashtable中,其实是用两个位置来表示一个真正的值的。因此容量就应该为table的长度的一半。
]//intcapacity=table.length>>1
intnewCapacity=capacity;
if(size>(capacity>>1)){
newCapacity=capacity*2;
}
[//if(size>(capacity>>1))
上面这段代码是判断是否需要扩充hashtable的容量。条件就是现在有效的值的个数已经超过了容量的一半。
]//if(size>(capacity>>1))
Object[]oldTable=this.table;
initializeTable(newCapacity);
this.tombstones=0;
[//this.tombstones=0;
这里是初始化了一个新的hashtable
]//this.tombstones=0;
if(size==0){
returntrue;
}
[//if(size==0)
如果当前没有有效的值,这直接返回
]//if(size==0)
for(inti=oldTable.length-2;i>=0;i-=2){
[//for(inti=oldTable.length-2;i>=0;i-=2)
在这个for循环里,将老的hashtable里的值拷贝到新的hashtable中去
]//for(inti=oldTable.length-2;i>=0;i-=2)
Objectk=oldTable[i];
if(k==null||k==TOMBSTONE){
continue;
}
[//if(k==null||k==TOMBSTONE)
如果发现是无效的则跳过
]//if(k==null||k==TOMBSTONE)
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>>reference=(Reference<ThreadLocal<?>>)k;
ThreadLocal<?>key=reference.get();
if(key!=null){
[//if(key!=null)
如果这个ThreadLocal对象没有被回收,还是有效的,则将这个值放到新的hashtable中去
]//if(key!=null)
add(key,oldTable[i+1]);
[//add(key,oldTable[i+1])
调用add函数来增加一个值.add函数的定义如下:
voidadd(ThreadLocal<?>key,Objectvalue){
for(intindex=key.hash&mask;;index=next(index)){
[//for(intindex=key.hash&mask;;index=next(index))
for循环查找一个可以插入的位置。初始的索引是用ThreadLocal的hash变量的值和Values的成员变量mask做与运算。由此可以看出,一类的ThreadLocal的值是放在一个索引位置上的。
next函数是计算下一个索引位置。next函数的定义如下:
privateintnext(intindex){
return(index+2)&mask;
}
正如我们之前分析的那样,在hashtable中其实是用两个位置来表示一个值的。
]//for(intindex=key.hash&mask;;index=next(index))
Objectk=table[index];
if(k==null){
table[index]=key.reference;
table[index+1]=value;
return;
}
[//if(k==null)
发现一个空位置,则将值写入到这里。
]//if(k==null)
}
}
]//add(key,oldTable[i+1])
}else{
[//else
这里是表示垃圾回收的情况
]//else
size--;
}
}
returntrue;
}
]//if(rehash())
return;
}
if(size==0){
return;
}
[//if(size==0)
size为0表示有效的值为空,则不需要往下进行了
]//if(size==0)
intindex=clean;
[//intindex=clean
在Values类的设计中,有一个成员变量clean,记录上次清理结束的位置。为什么要记录这个位置呢?效率会高多少呢?
]//intindex=clean
Object[]table=this.table;
for(intcounter=table.length;counter>0;counter>>=1,index=next(index)){
Objectk=table[index];
if(k==TOMBSTONE||k==null){
continue;//ontonextentry
}
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>>reference=(Reference<ThreadLocal<?>>)k;
if(reference.get()==null){
table[index]=TOMBSTONE;
table[index+1]=null;
tombstones++;
size--;
}
[//if(reference.get()==null)
这里发现被垃圾回收的,则将这个位置标记为TOMBSTONE.
]//if(reference.get()==null)
}
clean=index;
[//clean=index
记录下次清理的位置
]//clean=index
}
]//cleanUp()
returnvalue;
}
//ThetablechangedduringinitialValue().
put(key,value);
[//put(key,value)
如果发现在调用initialValue函数过程中,hashtable已经发生了改变,则直接将这个值插入到hashtable中.
put函数的实现如下:
voidput(ThreadLocal<?>key,Objectvalue){
cleanUp();
[//cleanUp()
put函数首先调用cleanUp函数
]//cleanUp()
intfirstTombstone=-1;
[//intfirstTombstone=-1
这里用一个变量firstTombstone记录发现的第一个TOMBSTONE的位置,为什么要记录这个位置呢?因为TOMBSTONE表示被垃圾回收之后的对该位置的表示。
]//intfirstTombstone=-1
for(intindex=key.hash&mask;;index=next(index)){
Objectk=table[index];
if(k==key.reference){
table[index+1]=value;
return;
}
[//if(k==key.reference)
这里发现了同样一个ThreadLocal对象,则直接替换值
]//if(k==key.reference)
if(k==null){
if(firstTombstone==-1){
//Fillinnullslot.
table[index]=key.reference;
table[index+1]=value;
size++;
return;
}
//Gobackandreplacefirsttombstone.
table[firstTombstone]=key.reference;
table[firstTombstone+1]=value;
tombstones--;
size++;
return;
}
[//if(k==null)
如果当前索引值位置的值为null,则说明发现了一个没有用过的位置,则不必继续搜索了。这里还会将值保存到合适的索引值的位置上去。
]//if(k==null)
//Rememberfirsttombstone.
if(firstTombstone==-1&&k==TOMBSTONE){
firstTombstone=index;
}
}
}
]//put(key,value)
returnvalue;
}
intfirstTombstone=-1;
for(index=next(index);;index=next(index)){
Objectreference=table[index];
if(reference==key.reference){
returntable[index+1];
}
if(reference==null){
Objectvalue=key.initialValue();
if(this.table==table){
if(firstTombstone>-1&&table[firstTombstone]==TOMBSTONE){
table[firstTombstone]=key.reference;
table[firstTombstone+1]=value;
tombstones--;
size++;
returnvalue;
}
if(table[index]==null){
table[index]=key.reference;
table[index+1]=value;
size++;
cleanUp();
returnvalue;
}
}
put(key,value);
returnvalue;
}
if(firstTombstone==-1&&reference==TOMBSTONE){
firstTombstone=index;
}
}
[//for(index=next(index);;index=next(index))
上面这段代码是不是看着很眼熟,和put函数中的实现是相似的。
]//for(index=next(index);;index=next(index))
}
]//values.getAfterMiss(this)
}
]//if(sThreadLocal.get()!=null)
thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");
}
sThreadLocal.set(newLooper(quitAllowed));
[//sThreadLocal.set(newLooper(quitAllowed))
如果sThreadLocal.get函数为null,则说明还没有为当前线程设置过Looper对象,则这里就调用ThreadLocal的set函数来为当前线程new一个Looper对象。
我们先来看一下Looper的构造函数:
privateLooper(booleanquitAllowed){
mQueue=newMessageQueue(quitAllowed);
[//mQueue=newMessageQueue(quitAllowed)
在Looper类中,有一个MessageQueue的成员变量mQueue,定义如下:
finalMessageQueuemQueue;
也就是说每一个Looper内部维护了一个MessageQueue。
我们看一下MessageQueue的构造函数:
MessageQueue(booleanquitAllowed){
mQuitAllowed=quitAllowed;
mPtr=nativeInit();
[//mPtr=nativeInit()
这里会调用nativeInit()函数。从名字可以猜测这是个native函数。
privatenativestaticlongnativeInit();
果不其然,确实是个native函数。
调用nativeInit函数其实是调用的android_os_MessageQueue.cpp(为与/home/yaojian/AndroidSource/frameworks/base/core/jni文件夹下)的android_os_MessageQueue_nativeInit函数:
staticjlongandroid_os_MessageQueue_nativeInit(JNIEnv*env,jclassclazz){
NativeMessageQueue*nativeMessageQueue=newNativeMessageQueue();
[//NativeMessageQueue*nativeMessageQueue=newNativeMessageQueue()
这里会创建一个C++层的NativeMessageQueue的对象。
我们先来看一下NativeMessageQueue的类结构:
classNativeMessageQueue:publicMessageQueue
classMessageQueue:publicRefBase
MessageQueue继承自RefBase类,由此可知可以用智能指针来管理MessageQueue类。
我们再来看一下NativeMessageQueue类的构造函数:
MessageQueue::MessageQueue(){
}
NativeMessageQueue::NativeMessageQueue():mInCallback(false),mExceptionObj(NULL){
mLooper=Looper::getForThread();
[//mLooper=Looper::getForThread()
在NativeMessageQueue类中也有一个Looper类型的成员变量mLooper,mLooper的定义如下:
sp<Looper>mLooper;
这里mLooper的初始化是通过Looper的getForThread函数来实现的:
sp<Looper>Looper::getForThread(){
intresult=pthread_once(&gTLSOnce,initTLSKey);
[//intresult=pthread_once(&gTLSOnce,initTLSKey)
这里这个pthread_once函数是干什么的呢?我也不知道,百度一下查出下面的解释:
intpthread_once(pthread_once_t*once_control,void(*init_routine)(void))
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。
也就是说pthread_once保证了initTLSKey函数之调用一次,gTLSOnce和initTLSKey的定义如下:
staticpthread_once_tgTLSOnce=PTHREAD_ONCE_INIT;
initTLSKey函数的实现如下:
voidLooper::initTLSKey(){
intresult=pthread_key_create(&gTLSKey,threadDestructor);
[//intresult=pthread_key_create(&gTLSKey,threadDestructor)
在initTLSKey函数中,会调用pthread_key_create函数。那么这个pthread_key_create函数有什么作用么?我也不知道啊?还是百度一下吧:
intpthread_key_create(pthread_key_t*key,void(*destructor)(void*));
函数pthread_key_create()用来创建线程私有数据。该函数从TSD池中分配一项,将其地址值赋给key供以后访问使用。第2个参数是一个销毁函数,它是可选的,可以为NULL,为NULL时,则系统调用默认的销毁函数进行相关的数据注销。如果不为空,则在线程退出时(调用pthread_exit()函数)时将以key锁关联的数据作为参数调用它,以释放分配的缓冲区,或是关闭文件流等。
gTLSKey和threadDestructor的定义如下:
staticpthread_key_tgTLSKey=0;
voidLooper::threadDestructor(void*st){
Looper*constself=static_cast<Looper*>(st);
if(self!=NULL){
self->decStrong((void*)threadDestructor);
}
}
]//intresult=pthread_key_create(&gTLSKey,threadDestructor)
LOG_ALWAYS_FATAL_IF(result!=0,"CouldnotallocateTLSkey.");
}
]//intresult=pthread_once(&gTLSOnce,initTLSKey)
LOG_ALWAYS_FATAL_IF(result!=0,"pthread_oncefailed");
return(Looper*)pthread_getspecific(gTLSKey);
[//return(Looper*)pthread_getspecific(gTLSKey)
在getForThread函数的最后会调用pthread_getspecific函数。那pthread_getspecific函数是干什么呢?又一次不知道,这次我决定不求助百度了,求助Google:
void*pthread_getspecific(pthread_key_tkey);
函数pthread_getspecific()将与key相关联的数据读出来。返回的数据类型都是void*,因此可以指向任何类型的数据。
]//return(Looper*)pthread_getspecific(gTLSKey)
}
[//sp<Looper>Looper::getForThread()
通过分析getForThread函数的实现,我们发现其实这个ThreadLocal的实现是很相似的。
那为什么还在C++层在实现一套呢?现在还不知道啊.
]//sp<Looper>Looper::getForThread()
]//mLooper=Looper::getForThread()
if(mLooper==NULL){
[//if(mLooper==NULL)
这里如果当前线程的Looper返回的空,则要为当前线程设置一个新的Looper对象
]//if(mLooper==NULL)
mLooper=newLooper(false);
[//mLooper=newLooper(false)
这里会先new一个C++层的Looper对象。我们看一下Looper的构造函数:
Looper::Looper(boolallowNonCallbacks):
mAllowNonCallbacks(allowNonCallbacks),mSendingMessage(false),
mResponseIndex(0),mNextMessageUptime(LLONG_MAX){
intwakeFds[2];
intresult=pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result!=0,"Couldnotcreatewakepipe.errno=%d",errno);
mWakeReadPipeFd=wakeFds[0];
mWakeWritePipeFd=wakeFds[1];
result=fcntl(mWakeReadPipeFd,F_SETFL,O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result!=0,"Couldnotmakewakereadpipenon-blocking.errno=%d",
errno);
result=fcntl(mWakeWritePipeFd,F_SETFL,O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result!=0,"Couldnotmakewakewritepipenon-blocking.errno=%d",
errno);
mIdling=false;
//Allocatetheepollinstanceandregisterthewakepipe.
mEpollFd=epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd<0,"Couldnotcreateepollinstance.errno=%d",errno);
structepoll_eventeventItem;
memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
eventItem.events=EPOLLIN;
eventItem.data.fd=mWakeReadPipeFd;
result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
LOG_ALWAYS_FATAL_IF(result!=0,"Couldnotaddwakereadpipetoepollinstance.errno=%d",
errno);
}
在Looper的构造函数中,会创建两个管道:mWakeReadPipeFd和mWakeWritePipeFd。并且利用epoll机制来设置监听mWakeReadPipeFd。
]//mLooper=newLooper(false)
Looper::setForThread(mLooper);
[//Looper::setForThread(mLooper)
当创建完C++层的Looper对象之后,就可以调用setForThread函数来将该对象设置到当前线程中去。
voidLooper::setForThread(constsp<Looper>&looper){
sp<Looper>old=getForThread();//alsohasside-effectofinitializingTLS
if(looper!=NULL){
looper->incStrong((void*)threadDestructor);
}
pthread_setspecific(gTLSKey,looper.get());
if(old!=NULL){
old->decStrong((void*)threadDestructor);
}
}
]//Looper::setForThread(mLooper)
}
}
]//NativeMessageQueue*nativeMessageQueue=newNativeMessageQueue()
if(!nativeMessageQueue){
jniThrowRuntimeException(env,"Unabletoallocatenativequeue");
return0;
}
nativeMessageQueue->incStrong(env);
returnreinterpret_cast<jlong>(nativeMessageQueue);
}
当创建完C++层的NativeMessageQueue之后,就得到这个C++层的NativeMessageQueue的句柄,并赋值给成员变量mPtr.mPtr的定义如下:
privatelongmPtr;//usedbynativecode
由此得出结论,每一个Java层的MessageQueue对象在C++曾都有一个NativeMessageQueue对象。
]//mPtr=nativeInit()
}
]//mQueue=newMessageQueue(quitAllowed)
mThread=Thread.currentThread();
}
[//privateLooper(booleanquitAllowed)
首先我们注意到Looper的构造函数是private的。
]//privateLooper(booleanquitAllowed)
当构造完Looper对象之后,就会调用ThreadLocal的set函数来将该Looper对象保存到当前线程的ThreadLocal中去。
我们来看一下ThreadLocal类的set函数的实现:
publicvoidset(Tvalue){
ThreadcurrentThread=Thread.currentThread();
Valuesvalues=values(currentThread);
if(values==null){
values=initializeValues(currentThread);
}
values.put(this,value);
}
[//publicvoidset(Tvalue)
set函数的实现还是比较清晰的,里面调用的函数我们之前都分析过。
]//publicvoidset(Tvalue)
]//sThreadLocal.set(newLooper(quitAllowed))
}