配色: 字号:
Android消息机制(基于源码解析)
2016-10-19 | 阅:  转:  |  分享 
  
Android消息机制(基于源码解析)

1.消息机制概述



Android中的消息机制主要指的是Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper、Message的支撑,下文会逐一分析。

2.为什么需要消息机制



Android中的消息机制主要是为了满足线程间通信而设计的,最重要的应用场景应该在于更新UI



Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常

系统为什么不允许在自线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。

那为什么不对UI控件的访问加上锁机制呢?缺点有两个:



首先加上锁机制会让UI访问的逻辑变得复杂;

其次锁机制会降低访问UI的效率,因为锁机制会阻塞某些现成的执行

鉴于这两个缺点,最简单且最高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只是需要通过Handler切换下UI的访问执行线程即可



3.Android中线程的分类



带有消息队列,用来执行循环型任务(例如主线程ActivityThread,Android.os.HandlerThread)有消息时就处理,没有消息时就睡眠

没有消息队列,用来执行一次性任务(例如java.lang.Thread)

任务一旦执行完成便退出

4.带有消息队列的线程具体实现



主要涉及4个方面



Message(消息)

MessageQueue(消息队列)

Looper(消息循环)

Handler(消息发送和处理)

4.1消息队列



说到MessageQueue,我们来看下它是干什么的



/

Low-levelclassholdingthelistofmessagestobedispatchedbya

{@linkLooper}.MessagesarenotaddeddirectlytoaMessageQueue,

butratherthrough{@linkHandler}objectsassociatedwiththeLooper.



YoucanretrievetheMessageQueueforthecurrentthreadwith

{@linkLooper#myQueue()Looper.myQueue()}.

/

它是一个低等级的持有Messages集合的类,被Looper分发。Messages并不是直接加到MessageQueue的,而是通过Handler对象和Looper关联到一起。我们可以通过Looper.myQueue()方法来检索当前线程的MessageQueue。



4.2Message



在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。我们看下这个类的注释



/



Definesamessagecontainingadescriptionandarbitrarydataobjectthatcanbe

senttoa{@linkHandler}.Thisobjectcontainstwoextraintfieldsandan

extraobjectfieldthatallowyoutonotdoallocationsinmanycases.



WhiletheconstructorofMessageispublic,thebestwaytoget

oneoftheseistocall{@link#obtainMessage.obtain()}oroneofthe

{@linkHandler#obtainMessageHandler.obtainMessage()}methods,whichwillpull

themfromapoolofrecycledobjects.

/

这个类定义了一个包含描述和一个任意类型对象的对象,它可以被发送给Handler。

从注释里我们还可以了解到以下几点:



尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。

如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存

用message.what来标识信息,以便用不同方式处理message

4.3Looper



/

Thisclasscontainsthecoderequiredtosetupandmanageaneventloop

basedonMessageQueue.APIsthataffectthestateofthequeueshouldbe

definedonMessageQueueorHandlerratherthanonLooperitself.Forexample,

idlehandlersandsyncbarriersaredefinedonthequeuewhereaspreparingthe

thread,looping,andquittingaredefinedonthelooper.

/

这个类是基于消息队列用来设置和管理事件循环的代码,对队列的改变应该在MessageQueue或Handler上定义,而不是在Looper本身定义,例如空间处理和同步障碍应该在队列上定义,然而准备线程、循环和退出在Looper本身定义,



4.4Handler



/

AHandlerallowsyoutosendandprocess{@linkMessage}andRunnable

objectsassociatedwithathread’s{@linkMessageQueue}.EachHandler

instanceisassociatedwithasinglethreadandthatthread’smessage

queue.WhenyoucreateanewHandler,itisboundtothethread/

messagequeueofthethreadthatiscreatingit–fromthatpointon,

itwilldelivermessagesandrunnablestothatmessagequeueandexecute

themastheycomeoutofthemessagequeue.







TherearetwomainusesforaHandler:(1)toschedulemessagesand

runnablestobeexecutedassomepointinthefuture;and(2)toenqueue

anactiontobeperformedonadifferentthreadthanyourown.







Schedulingmessagesisaccomplishedwiththe

{@link#post},{@link#postAtTime(Runnable,long)},

{@link#postDelayed},{@link#sendEmptyMessage},

{@link#sendMessage},{@link#sendMessageAtTime},and

{@link#sendMessageDelayed}methods.Thepostversionsallow

youtoenqueueRunnableobjectstobecalledbythemessagequeuewhen

theyarereceived;thesendMessageversionsallowyoutoenqueue

a{@linkMessage}objectcontainingabundleofdatathatwillbe

processedbytheHandler’s{@link#handleMessage}method(requiringthat

youimplementasubclassofHandler).







WhenpostingorsendingtoaHandler,youcaneither

allowtheitemtobeprocessedassoonasthemessagequeueisready

todoso,orspecifyadelaybeforeitgetsprocessedorabsolutetimefor

ittobeprocessed.Thelattertwoallowyoutoimplementtimeouts,

ticks,andothertiming-basedbehavior.







Whena

processiscreatedforyourapplication,itsmainthreadisdedicatedto

runningamessagequeuethattakescareofmanagingthetop-level

applicationobjects(activities,broadcastreceivers,etc)andanywindows

theycreate.Youcancreateyourownthreads,andcommunicatebackwith

themainapplicationthreadthroughaHandler.Thisisdonebycalling

thesamepostorsendMessagemethodsasbefore,butfrom

yournewthread.ThegivenRunnableorMessagewillthenbescheduled

intheHandler’smessagequeueandprocessedwhenappropriate.

/

这个有点长,简单概括下:每一个Handler实例关联了一个单一的ghread和这个thread的messagequeue,当Handler的实例被创建的时候它就被绑定到了创建它的thread。它用来调度message和runnables在未来某个时间点的执行,还可以排列其他线程里执行的操作。



Handler主要用的来管理某个线程(也可能是进程)的消息队列。

比如处理主线程的消息队列,包含消息的发送和接收过程。

消息的发送可以通过Post的一系列方法和Sende的一系列方法来实现。

而post的一系列方法最终是通过send的一系列方法来实现的。这样就可以将一些耗时任务放到其他线程之中,待任务完成之后就往主线程的消息队列中添加一个消息,这样Handler的Callback,即handleMessage就会被调用。但是Handler并不是线程安全的,因此建议将Handler作为一个静态内部类。

所以Handler只是处理消息,耗时任务放在其他线程。



4.5四元素的交互过程



具体工作过程



消息队列的创建

消息循环

消息的发送最基本的两个API

带一个Runnable参数,会被转换为一个Message参数

带一个Message参数,用来描述消息的内容

Handler.sendMessage

Handler.post

消息的处理

基于消息的异步任务接口



android.os.HandlerThread



适合用来处于不需要更新UI的后台任务

android.os.AyncTask



适合用来处于需要更新UI的后台任务

4.5.1Looper与消息队列的创建



首先确认当前线程是否具有Looper(主线程默认具有Looper)如果没有则创建

我们知道Android上一个应用的入口,应该是ActivityThread。和普通的Java类一样,入口是一个main方法。创建主线程源码示例:



publicstaticvoidmain(String[]args){



//~省略部分无关代码~



//创建Looper和MessageQueue对象,用于处理主线程的消息

Looper.prepareMainLooper();



//创建ActivityThread对象

ActivityThreadthread=newActivityThread();



//建立Binder通道(创建新线程)

thread.attach(false);



if(sMainThreadHandler==null){

sMainThreadHandler=thread.getHandler();

}



if(false){

Looper.myLooper().setMessageLogging(new

LogPrinter(Log.DEBUG,"ActivityThread"));

}



//EndofeventActivityThreadMain.

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);



//消息循环运行

Looper.loop();



thrownewRuntimeException("Mainthreadloopunexpectedlyexited");

}



我们可以看到主方法首先通过Looper.prepareMainLooper()初始化了我们主线程(UI)的Looper并且启动它。然后就可以处理子线程和其他组件发来的消息了



如果不是主线程的两线程进行通信,可以通过以下方式来创建



classLooperThreadextendsThread{



publicHandlermHandler;



publicvoidrun(){



//将当前线程初始化为Looper线程

Looper.prepare();



//...其他处理,如实例化handler

mHandler=newHandler(){

publicvoidhandleMessage(Messagemsg){

//processincomingmessageshere

}

};



//开始循环处理消息队列

Looper.loop();

}

}



Looper源码如下:



publicfinalclassLooper{



privatestaticfinalStringTAG="Looper";



//sThreadLocal.get()willreturnnullunlessyou''vecalledprepare().

staticfinalThreadLocalsThreadLocal=newThreadLocal();

privatestaticLoopersMainLooper;//guardedbyLooper.class



//Looper内的消息队列

finalMessageQueuemQueue;

//当前线程

finalThreadmThread;



privatePrintermLogging;



privateLooper(booleanquitAllowed){

mQueue=newMessageQueue(quitAllowed);

mThread=Thread.currentThread();

}



/Initializethecurrentthreadasalooper.

Thisgivesyouachancetocreatehandlersthatthenreference

thislooper,beforeactuallystartingtheloop.Besuretocall

{@link#loop()}aftercallingthismethod,andenditbycalling

{@link#quit()}.

/

publicstaticvoidprepare(){

prepare(true);

}



privatestaticvoidprepare(booleanquitAllowed){

//试图在有Looper的线程中再次创建Looper将抛出异常

if(sThreadLocal.get()!=null){

thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");

}

sThreadLocal.set(newLooper(quitAllowed));

}



/

Initializethecurrentthreadasalooper,markingitasan

application''smainlooper.Themainlooperforyourapplication

iscreatedbytheAndroidenvironment,soyoushouldneverneed

tocallthisfunctionyourself.Seealso:{@link#prepare()}

/

publicstaticvoidprepareMainLooper(){

prepare(false);

synchronized(Looper.class){

if(sMainLooper!=null){

thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared.");

}

sMainLooper=myLooper();

}

}



//~省略部分无关代码~

}



从中我们可以看到以下几点:



prepare()其核心就是将looper对象定义为ThreadLocal

一个Thread只能有一个Looper对象

prepare()方法会调用Looper的构造方法,初始化一个消息队列,并且指定当前线程

ThreadLocal并不是一个Thread,而是Thread的局部变量。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本。

所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

在调用Looper.loop()方法之前,确保已经调用了prepare(booleanquitAllowed)方法,并且我们可以调用quite方法结束循环



接下来再看看Looper.loop()



/

Runthemessagequeueinthisthread.Besuretocall

{@link#quit()}toendtheloop.

/

publicstaticvoidloop(){

//得到当前线程Looper

finalLooperme=myLooper();

if(me==null){

thrownewRuntimeException("NoLooper;Looper.prepare()wasn''tcalledonthisthread.");

}

//得到当前looper的MessageQueue

finalMessageQueuequeue=me.mQueue;



//Makesuretheidentityofthisthreadisthatofthelocalprocess,

//andkeeptrackofwhatthatidentitytokenactuallyis.

Binder.clearCallingIdentity();

finallongident=Binder.clearCallingIdentity();



//开始循环

for(;;){

Messagemsg=queue.next();//mightblock

if(msg==null){

//Nomessageindicatesthatthemessagequeueisquitting.

//没有消息表示消息队列正在退出

return;

}



//Thismustbeinalocalvariable,incaseaUIeventsetsthelogger

Printerlogging=me.mLogging;

if(logging!=null){

logging.println(">>>>>Dispatchingto"+msg.target+""+

msg.callback+":"+msg.what);

}



//将真正的处理工作交给message的target,即handler

msg.target.dispatchMessage(msg);



if(logging!=null){

logging.println("<<<<
}



//Makesurethatduringthecourseofdispatchingthe

//identityofthethreadwasn''tcorrupted.

finallongnewIdent=Binder.clearCallingIdentity();

if(ident!=newIdent){

Log.wtf(TAG,"Threadidentitychangedfrom0x"

+Long.toHexString(ident)+"to0x"

+Long.toHexString(newIdent)+"whiledispatchingto"

+msg.target.getClass().getName()+""

+msg.callback+"what="+msg.what);

}



//回收消息资源

msg.recycleUnchecked();

}

}



通过这段代码可知,调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MessageQueue中取出队头的消息(或者说是任务)执行。



除了prepare()和loop()方法,Looper类还有一些比较有用的方法,比如



Looper.myLooper()得到当前线程looper对象

getThread()得到looper对象所属线程

quit()方法结束looper循环这里需要注意的一点是,quit()方法其实调用的是MessageWueue的quite(booleansafe)方法。



voidquit(booleansafe){

if(!mQuitAllowed){

thrownewIllegalStateException("Mainthreadnotallowedtoquit.");

}



synchronized(this){

if(mQuitting){

return;

}

mQuitting=true;



if(safe){

removeAllFutureMessagesLocked();

}else{

removeAllMessagesLocked();

}



//WecanassumemPtr!=0becausemQuittingwaspreviouslyfalse.

nativeWake(mPtr);

}

}



我们看到其实主线程是不能调用这个方法退出消息队列的。至于mQuitAllowed参数是在Looper初始化的时候初始化的,主线程初始化调用的是Looper.prepareMainLooper()方法,这个方法把参数设置为false。



4.5.2消息的发送和处理(Handler)



从MessageQueue的注释中,我们知道添加消息到消息队列是通过Handler来操作的。我们通过源码来看下具体是怎么实现的



publicclassHandler{



//~省略部分无关代码~



finalMessageQueuemQueue;

finalLoopermLooper;



publicHandler(){

this(null,false);

}



publicHandler(Looperlooper){

this(looper,null,false);

}



publicHandler(booleanasync){

this(null,async);

}



publicHandler(Callbackcallback,booleanasync){

if(FIND_POTENTIAL_LEAKS){

finalClassklass=getClass();

if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&

(klass.getModwww.shanxiwang.netifiers()&Modifier.STATIC)==0){

Log.w(TAG,"ThefollowingHandlerclassshouldbestaticorleaksmightoccur:"+

klass.getCanonicalName());

}

}



mLooper=Looper.myLooper();

if(mLooper==null){

thrownewRuntimeException(

"Can''tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()");

}

mQueue=mLooper.mQueue;

mCallback=callback;

mAsynchronous=async;

}



publicHandler(Looperlooper,Callbackcallback,booleanasync){

mLooper=looper;

mQueue=looper.mQueue;

mCallback=callback;

mAsynchronous=async;

}



//~省略部分无关代码~

}



先看构造方法,其实里边的重点是初始化了两个变量,把关联looper的MessageQueue作为自己的MessageQueue,因此它的消息将发送到关联looper的MessageQueue上。



有了handler之后,我们就可以使用Handler提供的post和send系列方法向MessageQueue上发送消息了。其实post发出的Runnable对象最后都被封装成message对象



接下来我们看一下handler是如何发送消息的



/

CausestheRunnablertobeaddedtothemessagequeue.

Therunnablewillberunonthethreadtowhichthishandleris

attached.



@paramrTheRunnablethatwillbeexecuted.



@returnReturnstrueiftheRunnablewassuccessfullyplacedintothe

messagequeue.Returnsfalseonfailure,usuallybecausethe

looperprocessingthemessagequeueisexiting.

/

publicfinalbooleanpost(Runnabler)

{

returnsendMessageDelayed(getPostMessage(r),0);

}



/

Enqueueamessageintothemessagequeueafterallpendingmessages

before(currenttime+delayMillis).Youwillreceiveitin

{@link#handleMessage},inthethreadattachedtothishandler.



@returnReturnstrueifthemessagewassuccessfullyplacedintothe

messagequeue.Returnsfalseonfailure,usuallybecausethe

looperprocessingthemessagequeueisexiting.Notethata

resultoftruedoesnotmeanthemessagewillbeprocessed--if

thelooperisquitbeforethedeliverytimeofthemessage

occursthenthemessagewillbedropped.

/

publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)

{

if(delayMillis<0){

delayMillis=0;

}

returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);

}



/

Enqueueamessageintothemessagequeueafterallpendingmessages

beforetheabsolutetime(inmilliseconds)uptimeMillis.

Thetime-baseis{@linkandroid.os.SystemClock#uptimeMillis}.

Timespentindeepsleepwilladdanadditionaldelaytoexecution.

Youwillreceiveitin{@link#handleMessage},inthethreadattached

tothishandler.



@paramuptimeMillisTheabsolutetimeatwhichthemessageshouldbe

delivered,usingthe

{@linkandroid.os.SystemClock#uptimeMillis}time-base.



@returnReturnstrueifthemessagewassuccessfullyplacedintothe

messagequeue.Returnsfalseonfailure,usuallybecausethe

looperprocessingthemessagequeueisexiting.Notethata

resultoftruedoesnotmeanthemessagewillbeprocessed--if

thelooperisquitbeforethedeliverytimeofthemessage

occursthenthemessagewillbedropped.

/

publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis){

MessageQueuequeue=mQueue;

if(queue==null){

RuntimeExceptione=newRuntimeException(

this+"sendMessageAtTime()calledwithnomQueue");

Log.w("Looper",e.getMessage(),e);

returnfalse;

}

returnenqueueMessage(queue,msg,uptimeMillis);

}



privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){

msg.target=this;

if(mAsynchronous){

msg.setAsynchronous(true);

}

returnqueue.enqueueMessage(msg,uptimeMillis);

}



这里我们只列出了一种调用关系,其他调用关系大同小异,我们来分析一下



调用getPostMessage(r),把runnable对象添加到一个Message对象中。

sendMessageDelayed(getPostMessage(r),0),基本没做什么操作,又继续调用sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis)方法,在这个方法里拿到创建这个Handler对象的线程持有的MessageQueue。

调用enqueueMessage(queue,msg,uptimeMillis)方法,给msg对象的target变量赋值为当前的Handler对象,然后放入到MessageQueue。

那发送消息说完了,那我们的消息是怎样被处理的呢?



我们看到message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码。



/

CallbackinterfaceyoucanusewheninstantiatingaHandlertoavoid

havingtoimplementyourownsubclassofHandler.



@parammsgA{@linkandroid.os.MessageMessage}object

@returnTrueifnofurtherhandlingisdesired

/

publicinterfaceCallback{

publicbooleanhandleMessage(Messagemsg);

}



/

Subclassesmustimplementthistoreceivemessages.

/

publicvoidhandleMessage(Messagemsg){

}



/

Handlesystemmessageshere.

/

publicvoiddispatchMessage(Messagemsg){

if(msg.callback!=null){

handleCallback(msg);

}else{

if(mCallback!=null){

if(mCallback.handleMessage(msg)){

return;

}

}

handleMessage(msg);

}

}



privatestaticvoidhandleCallback(Messagemessage){

message.callback.run();

}



我们看到这里最终又调用到了我们重写的handleMessage(Messagemsg)方法来做处理子线程发来的消息或者调用handleCallback(Messagemessage)去执行我们子线程中定义并传过来的操作



为什么主线程不会因为Looper.loop()里的死循环卡死或者不能处理其他事务



这里涉及到的东西比较多,概括的理解是这样的



handler机制是使用pipe来实现的,主线程没有消息处理时会阻塞在管道的读端。

binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回。

主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。



既然是死循环又如何去处理其他事务呢?答案是通过创建新线程的方式。

我们看到main方法里调用了thread.attach(false),这里便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程。

献花(0)
+1
(本文系网络学习天...首藏)