源码讲解Handler机制


从事Android开发经常使用的Handler来进行异步操作,网上也有较多的说明,但是讲解得都太浅了,只能知道期大概原理,本章从源码角度来分析Hnadler机制。牵扯到的类有Handler,Message, MessageQueue, Looper。

Handler : 一个Handler允许在允许的线程中发送跟处理消息和Runnable对象。每个Handler对象跟单个线程以及线程中的消息队列关联在一起。

Message: 消息包含一个描述和任意的数据对象,可以使用Handler发送到消息队列中。这个对象包含两个额外的int字段和一个额外Object字段。

MessageQueue:用来存放线程放入的消息,要通过handler  sendMessage来添加消息,它的处理跟Looper有关。

Looper: 类用于运行一个消息循环的线程。

先分析Hanlder类:

其构造方法有好几个:


public Handler() {}

publicHandler(Callback callback) {}

publicHandler(Looper looper) {}

publicHandler(booleanasync){}

publicHandler(Callback callback,booleanasync) {}

publicHandler(Looper looper, Callback callback,booleanasync) {}

需要注意的地方,构造可以看出有一个Looper对象的构造,此构造会把Hanlder跟Looper绑定,如果是子线程中的Looper那么Handler处理的消息也将在子线程中。

Callback对象如果不为空的话,将使用Callback来处理Message。

大家经常使用有 sendEmpryMessage系列,SendMessage系列,post(Runnable)系列,而这些所有的发送消息的方法最后是都运行到 public boolean sendMessageAtTime(Message msg,long uptimeMillis),在此方法内把Message 插入到MessageQueue中,并根据uptimeMillis来判断插入的位置。

先分析一下各种send 跟 post 如何跳转到sendMessageAtTime这个方法的。

public final boolean sendEmptyMessage(intwhat){

return sendEmptyMessageDelayed(what,0);} //sendEmptyMessage(what) 其实跟sendEmptyMessageDelayed是一样的,只不过延时为0

再看public final boolean sendEmptyMessageDelayed(intwhat,long delayMillis) {

Message msg = Message.obtain();

msg.what= what;

return sendMessageDelayed(msg, delayMillis);}

把What进行了一下封装,毕竟Message存的都是Message

public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg,0);}

再看public final boolean sendMessageDelayed(Message msg,longdelayMillis){

if(delayMillis <0) { delayMillis = 0;}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

// 所谓延时就是在现在的基础上 加上要延时的时间就是要执行的时间

public boolean sendMessageAtTime(Message msg,longuptimeMillis) {

MessageQueue queue =mQueue;

if(queue ==null) {

RuntimeException e =newRuntimeException(

this+" sendMessageAtTime() called with no mQueue");

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

return false;

}

    returnenqueueMessage(queue, msg, uptimeMillis);

}

sendMesage系列跟sendEmptyMessage系列到sendMessageAtTime()的核心代码都已经贡上。

再看post系列public final boolean post(Runnable r){

return sendMessageDelayed(getPostMessage(r),0);}

private staticMessage getPostMessage(Runnable r) {

Message m = Message.obtain();

m.callback= r;

returnm;}

这个就不难理解了,获取一个新的Message把Runnable放置到的Message中,再调用SendMessage系列的函数。

同样的post系列就不多解析了,上源码 :

public final boolean postDelayed(Runnable r,longdelayMillis){

return sendMessageDelayed(getPostMessage(r), delayMillis);}

public final boolean postAtFrontOfQueue(Runnable r)

{ return sendMessageAtFrontOfQueue(getPostMessage(r));}

public final boolean sendMessageAtFrontOfQueue(Message msg) {

MessageQueue queue =mQueue;

if(queue ==null) {

RuntimeException e =newRuntimeException(

this+" sendMessageAtTime() called with no mQueue");

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

return false;

}

return enqueueMessage(queue, msg,0);

}  // 此处直接调用到 enqueueMessage(queue, msg,0) 没有走到sendMessageAtTime()


现在来说下private boolean enqueueMessage(MessageQueue queue, Message msg,longuptimeMillis) {

msg.target=this;

if(mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

} 最终将调用到消息队列的enqueueMessage方法。

在讲解enqueueMessage的方法之前,要先把Message这个类要说下,要处理的数据来源都是此类的。


public final classMessageimplementsParcelable {

public int what;

public int arg1;

public int arg2;

public Object obj;

/*package*/long when;

/*package*/Bundle data;

/*package*/Handler target;

/*package*/Runnable callback;

// sometimes we store linked lists of these things

/*package*/Message next;

} what arg1 arg2 obj这几个方法大家都熟悉,就是要处理的数据,里面还有个Bundle data,大家也是可以用来通过 getData() /peekData() 跟 setData() 存取数据。 而when呢,就是enqueueMessage函数的最后一个参数,用来表示是什么时候要运行的,插入到MessageQueue跟MessageQueue中取出Message也是通过此变量来做判断依据的。target 就是代表被哪个Handler放置到消息队列中的,在Message被取出时,也就是由那个Handle对象来处理的。而callback呢? 在前面的代码中应该看见过了( 见getPostMessage),post的runnable就是赋值给了message的callback,具体什么时候被调用请往下看。

next变量 这个又是啥东西呢,熟悉列表跟队列的同志应该清楚,列表只用一个引用一个头就好了,那么MesageQueue也是一样的,MessageQueue中其实只存了一个Message对象,遍历是通过其next对象来遍历的。

还有几个大家可能使用到函数的代码也贴一下源码,都比较简单,就请各位自己品味了。

public Bundle getData() { // 得到bundle数据

if(data==null) {

data=newBundle();

}

return data;

}

public voidsetData(Bundle data) {

this.data= data;

}

publicBundle peekData() {

returndata;

}


public voidsendToTarget() {

target.sendMessage(this);

}


public staticMessage obtain() {  //从全局池返回一个新的消息实例。让我们在许多情况下避免分 //配新对象。

synchronized(sPoolSync) {

if(sPool!=null) {

Message m =sPool;

sPool= m.next;

m.next=null;

m.flags=0;// clear in-use flag

sPoolSize--;

return m;

}

}

return newMessage();

}


现在说一下MessageQueue,通过Handle跟Message的一些说明,那么了解MessageQueue就简单多了,其实也是蛮复杂的,但是我们只关心我们要关心的就可以了,只要看enqueueMessage这个方法就好了。

boolean enqueueMessage(Message msg, longwhen) {

// xxxx 

//xxx


msg.markInUse();

msg.when= when;

Message p =mMessages;

boolean needWake;

if(p ==null|| when ==0|| when < p.when) {

// New head, wake up the event queue if blocked.

msg.next= p;

mMessages= msg;

needWake =mBlocked;

}else{

// Inserted within the middle of the queue.  Usually we don't have to wake

// up the event queue unless there is a barrier at the head of the queue

// and the message is the earliest asynchronous message in the queue.

needWake =mBlocked&& p.target==null&& msg.isAsynchronous();

Message prev;

for(;;) {

prev = p;

p = p.next;

if(p ==null|| when < p.when) {

break;

}

if(needWake && p.isAsynchronous()) {

needWake =false;

}

}

msg.next= p;// invariant: p == prev.next

prev.next= msg;

}

// We can assume mPtr != 0 because mQuitting is false.

if(needWake) {

nativeWake(mPtr);

}

}

return true;

}

主要做的就是根据when来确定新插进来的Message要插入到那个地方(需要注意的MessageQueue只有队列头的引用),并给When赋值给Message的when。


现在来看最终的Looper,里面主要有一个如何取出Message的方法,比较复杂

looper里面有一个MessageQueue,调用Loop方法后就开启了循环检测看Loop方法

/**

* Run the message queue in this thread. Be sure to call

* {@link#quit()} to end the loop.

*/

public static voidloop() {

finalLooper me =myLooper();

if(me ==null) {

throw newRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

}

finalMessageQueue queue = me.mQueue;

// Make sure the identity of this thread is that of the local process,

// and keep track of what that identity token actually is.

Binder.clearCallingIdentity();

final longident = Binder.clearCallingIdentity();

for(;;) {

Message msg = queue.next();// might block

if(msg ==null) {

// No message indicates that the message queue is quitting.

return;

}

// This must be in a local variable, in case a UI event sets the logger

Printer logging = me.mLogging;

if(logging !=null) {

logging.println(">>>>> Dispatching to "+ msg.target+" "+

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

}

msg.target.dispatchMessage(msg);

if(logging !=null) {

logging.println("<<<<< Finished to "+ msg.target+" "+ msg.callback);

}

// Make sure that during the course of dispatching the

// identity of the thread wasn't corrupted.

final longnewIdent = Binder.clearCallingIdentity();

if(ident != newIdent) {

Log.wtf(TAG,"Thread identity changed from 0x"

+ Long.toHexString(ident) +" to 0x"

+ Long.toHexString(newIdent) +" while dispatching to "

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

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

}

msg.recycleUnchecked();

}

}

可以看到在for循环是个死循环里面都要用了MessageQueue的Next方法,看MessageQueue的next方法

Message next() {

// Return here if the message loop has already quit and been disposed.

// This can happen if the application tries to restart a looper after quit

// which is not supported.

final longptr =mPtr;

if(ptr ==0) {

return null;

}

intpendingIdleHandlerCount = -1;// -1 only during first iteration

intnextPollTimeoutMillis =0;

for(;;) {

if(nextPollTimeoutMillis !=0) {

Binder.flushPendingCommands();

}

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized(this) {

// Try to retrieve the next message.  Return if found.

final longnow = SystemClock.uptimeMillis();

Message prevMsg =null;

Message msg =mMessages;

if(msg !=null&& msg.target==null) {

// Stalled by a barrier.  Find the next asynchronous message in the queue.

do{

prevMsg = msg;

msg = msg.next;

}while(msg !=null&& !msg.isAsynchronous());

}

if(msg !=null) {

if(now < msg.when) {

// Next message is not ready.  Set a timeout to wake up when it is ready.

nextPollTimeoutMillis = (int) Math.min(msg.when- now, Integer.MAX_VALUE);

}else{

// Got a message.

mBlocked=false;

if(prevMsg !=null) {

prevMsg.next= msg.next;

}else{

mMessages= msg.next;

}

msg.next=null;

if(DEBUG) Log.v(TAG,"Returning message: "+ msg);

msg.markInUse();

returnmsg;

}

}else{

// No more messages.

nextPollTimeoutMillis = -1;

}

// Process the quit message now that all pending messages have been handled.

if(mQuitting) {

dispose();

return null;

}

// If first time idle, then get the number of idlers to run.

// Idle handles only run if the queue is empty or if the first message

// in the queue (possibly a barrier) is due to be handled in the future.

if(pendingIdleHandlerCount <0

&& (mMessages==null|| now

pendingIdleHandlerCount =mIdleHandlers.size();

}


}

mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);

}


if(!keep) {

synchronized(this) {

mIdleHandlers.remove(idler);

}

}

}

// Reset the idle handler count to 0 so we do not run them again.

pendingIdleHandlerCount =0;

// While calling an idle handler, a new message could have been delivered

// so go back and look again for a pending message without waiting.

nextPollTimeoutMillis =0;

}

}


说实在话没有看懂,但是结果就是Loop根据When循环出去MessageQueue里面的Message

取出后调用msg.target.dispatchMessage(msg);

也就是Handler里面的派发消息 dispatchMessage。转了半天终于回到了Handler。有几个地方要注意的一个线程只有一个MessageQueue 一个Looper, 但是Handler跟Message是可以有多个。来看dispathMessage这个方法:

public voiddispatchMessage(Message msg) {

if(msg.callback!=null) {

handleCallback(msg);

}else{

if(mCallback!=null) {

if(mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

private static voidhandleCallback(Message message) {

message.callback.run();

}

}

以上代码的意思是  如果Message中的callback不为空,就调用callback中的run方法,也就是Post里面的那个Runable

如果Handler里面的Callback(前面构造有说)不为空,就是调用CallBack里面的handleMessage() 方法, 如果不是以上2种情况就是Handler自己要重载处理handleMessage()的方法

从SendMessage到HandlerMesage的方法都已经说完了。总体来说

1.Handler发送各种消息, 然后对各种消息进行封装成Message对象

2.根据时间把Mesage插入到Looper的MessageQueue中,并把Message标上时间

3.loop循环取出Message(此步骤最为复杂了,好几个地方没有想通,里面也调用了好几个native方法)

4.取出之后对Message进行派发处理。

打个比喻:有3个人(Hanlder)H1,H2,H3, 他们有一些包裹(Message),每个包裹上都标注了什么时候取出来,H1, H2,H3把他们的包裹存进银行(MessageQueue)里,存进银行的时候包裹上被标记是谁(H1, H2, H3)的包裹,并按照要取出的时间的顺序进行了排序,银行的工作人员(Loop),会不断的检查包裹是否要取出来了,到了指定的时间取出时间,包裹被工作人员打开。根据包裹上标记的,寄送给被标记的人H1 H2 或者H3。在H1 H2 或者H3收到之后,查看包裹里面是否写有包裹里面的东西要给谁(Message的CallBack),如果包裹上写了要给谁,那么就给那个人处理(Message的CallBack也就运行了)。如果没有写给谁出来,那么H1看下是否有认识的人(H1的Callback)能处理这个包裹,如果有那就给CallBack处理,如果没有那么就只能自己来出来了(要重载HandleMessage方法)。

另外要注意的是post 的runnable的那个Message的what方法是没有赋值的,也就是默认值0, 所以最好大家自己发送message的时候不要把message的what赋值为0 或者不赋值,以免引起不必要的麻烦。

Handler是可以跟子线程绑定在一起的,并不是只能运行在UI线程中的,Android的UI线程默认是调用了prepare()跟Loop()的。

例如 class LooperThread extends Thread {

   public Handler mHandler;

      public void run() {

          Looper.prepare();

      mHandler = new Handler() {

             public void handleMessage(Message msg) {

                  // process incoming messages here

            }

          };

         Looper.loop();

     }

插个图:解析 2个Handler对象H1 H2向MessageQueue发送Message,此时MessageQueue中已经有3个Message了其中有一个来自H1 2个来自H2.而且H1的一个Message被取出了,根据Message的信息做对应的处理。


不得不吐槽一下简书的排版,代码copy过来的排版真不好处理(不知道是不是我会用),希望官方给一个好的排版。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容