[图解法结合源码]理解、记忆Handler、Looper、MessageQueue之间的关系

>[图解法结合源码理解、记忆Handler、Looper、MessageQueue之间的关系]

看了不少关于Handler、Looper、MessageQueue之间关系的文章。感觉挺枯燥的,上来就是一团代码,看着心烦。

后来我捋了捋,画了个图。先看图,我们再来谈他们间的关系:

 

在这个图中,我做了个类比:(很重要,多看几遍)

MessageQueue,流水线上的"履带";

Looper,履带的"驱动轮";

Handler,流水线上的"工人";

Message,流水线上的"包裹"。

 

现在让我们来解释这三者间的工作关系,首先,我们要明确一个基本法:

一个Thread只能有且只能一个Looper。一个Looper只能对应一个Message。一个Looper和MessageQueue的绑定体可以对应多个Handler。(参考上图)

 

1.Looper与MessageQueue

刚刚说了一个Thread只能有一个Looper。为什么只能有一个呢?让我们去看它的一个方法Looper.prepare()方法的源码:

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(true));  
}  

第二行,如果当前线程已有一个Looper,那么将直接抛出异常。

然后我们再来说说一个线程一般要怎么创建一个Looper,例子A:(下面会拿这个例子讲解)

class LooperThread extends Thread  {  
    public Handler mHandler;  
    public void run()   {  
        Looper.prepare();  
        mHandler = new Handler()   {  
            public void handleMessage(Message msg) {  
              // 你的方法
            }  
        };  
    }    
    Looper.loop();  
}  

首先,一个线程先Looper.prepare(),创建一个Looper,在这个prepare()方法中,回头看一眼第一段里的代码,它会在 return 中new 一个(Looper(true));其实,就是下面的代码:

private Looper(boolean quitAllowed) {  
        mQueue = new MessageQueue(quitAllowed); 
        mRun = true;
        mThread = Thread.currentThread();  //绑定当前线程
}  

注意第二行,它创建并绑定了一个MessageQueue,做个类比,就是给Looper这个驱动轮套上了它的履带MessageQueue,由于Looper在当前线程唯一,则其应为一一对应关系。

 

现在驱动轮有了,履带有了,要让这个流水线动起来,显然,还需要另外一个操作。

没错,这个操作就是例子A中倒二行的Looper.loop()。其源码为:(仅列出你需要理解的代码,其他你不需要关心)

public static void loop() {  
        final Looper me = myLooper();//获取当前线程的Looper
        if (me == null) {  //如果没有为当前线程进行Looper.prepare(),跳出异常
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
        }
        final MessageQueue queue = me.mQueue; //获得当前Looper me所绑定的MessageQueue
        for (;;) {  //无限循环
            Message msg = queue.next(); // 通过MessageQueue的next()方法从队首取一个消息
            if (msg == null) {  
                //如果消息队列为空,就退出这个无限循环
                return;  
            }  
            msg.target.dispatchMessage(msg);  //分发消息,msg.target其实就是个handler,你先记着不要管
            msg.recycle();  //通过MessageQueue的recycle()方法回收资源,让"履带"转动取来
        }  
}  

Tips:这里需要注意一个事情,观察到loop()方法里有个无限循环。在例子A中,如果你在Looper.loop()后面写代码,IDE会判断这些代码是不会执行的,因此会报错。也就说,loop()方法必须在该线程需要执行的内容的最后一行写!!!

 

上面的loop()中我给出了详细的注解,稍微花两分钟就能看明白。第8行,从MessageQueue队列中取走第一个消息,然后在第13行,调用msg.target的disspachMessage()方法,分发这个消息。其实msg.target就是个handler,至于为什么,我会在Handler的部分进行讲解。

Looper部分总结一句话:

在线程的开头,为这个线程准备一个Looper(Looper.prepare()),这个Looper会自动绑定上一个MessageQueue,然后在线程的最后,通过Looper.loop()方法,让这个MessageQueue转动起来,传送这个MessageQueue上的消息对象Message。

每次Message被传送到队首,这个Message会被disspatchMessage()方法分发出去,分配到管理它的Handler(流水线工人)那里进行处理。
 

 

2.Message

我们在图中做了一个类比,把Message当成了一个包裹,那么它里面到底包裹了啥?我们来看一下它的源码:

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;//以上是一些数据类型,无视
    public Messenger replyTo;
    Bundle data;//一般用于封装数据的包
    Handler target;//注意看这里,是个Handler
    Runnable callback;//注意这个回调
    ......
}

看到那个target了么,就是个Handler。现在回去看我的那个图,你会看到我在Handler("流水线工人")那里写了个盖章二字,是什么意思呢?

 

一个handler通过post方法(我们下面再说,总是就是线程把这个消息Message丢给了Handler)拿到一个Message之后,这个Handler并不是马上处理这个消息,而是先盖上一个章:

msg.target=this;

然后把这个消息丢向流水线的"履带"——MessageQueue,让它排队去,等它排到队首之后,再通过msg.target.dispatchMessage()方法分到刚刚送它进流水线的“工人”手里进行处理。

 

有人可能会问了,Handler拿到Message为什么不马上处理呢?

原因是:本身Handler并没有提供并发解决机制,但MessageQueue的next()提供了并发解决机制。稍微理解一下这句话,很容易理解的。要知道Handler不只能接收到本线程丢过来的Msg包,还能接到其他线程(一般是子线程)丢过来的包(参考第一幅图),你不搞个基本法,排队来解决,这个程序怕是药丸。

这里还要注意一下那个Runnable callback的回调,稍后会有解释。

 

Message要注意的只有两种用法,一个是把信息封装,一个是解包提取信息;

Message msg = Message.obtain();//尽量不要一直new Message(),原因很简单,省内存。
Bundle b=new Bundle();//信息封包
b.putInt("Yidong",10086);
msg.setData(b);
//msg.sendToTarget();
myHandler.post(msg);//先丢给流水线工人盖章、入列
Bundle b=msg.getData();//在handlerMessage()方法中解包提取信息。至于这个方法是啥,马上就要说到了。
int a=b.getInt("Yidong");//a=10086

一句话总结,Message就是个"包裹",里面装了你需要传递信息,然后被丢来丢去(╯‵□′)╯︵,最后被分配到它的工人那里拆包进行处理。

 

3.Handler

终于,这篇文章要写完了。

接下来,我们要说我们的主角了——Handler了。大部分时候,你不需要关心MessageQueue和Looper是怎么工作的,但是Handler是你时时刻刻都必须打招呼的家伙。

首先是Handler究竟是个什么家伙。我把它类比为流水线上"工人" ,它即负责把收到的消息入列,也负责处理队首属于自己的那一份Message。

它的构造函数里没有啥东西,你只需要知道,它会先获取当前线程的Looper并绑定,然后从这个Looper获取到它的MessageQueue。然后,Handler与Looper、MessageQueue的关系就建立起来了。另外,他还创建了一个mCallback。这个待会儿再说。

刚刚我们一直在说msg.target.dispatchMessage()方法(其实也就是msg绑定的handler的disspatchMessage()),那么这个方法到底是个啥东西呢?看源码:

public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            handleMessage(msg);  
        }  
    }  

第2行,看到那个callback了没有?是不是!很!熟!悉!我刚刚让你注意了有木有?还记得它是啥么?它其是个Runnable。

 

这一整段的代码就是告诉你:要分发这个代码,先看这个包裹的callback有没有绑定上啥东西(一个Runable);如果有,直接交由这个Runable进行处理,如果没有,查看当前handler的mCallback有没有绑定上啥东西;如果有,交由这个mCallback处理,如果没有,那么就调用当前handler的handleMessage()方法处理。

你可能要问,这个mCallback到底是个啥玩意?其实就是这个玩意:

public interface Callback {
    public boolean handleMessage(Message msg);
}

讲白了就是这个handler"工人"的上一个"工人","包裹"Message被丢来丢去地传递,可能它真正要处理它的工人并不是当前给它盖章的工人,而是上一个.....不过,一般情况下我们并不会遇到这种情况。

 

现在要说的是handleMessage()方法,它的源码:

public void handleMessage(Message msg) { 
    //填入你自己的方法
 }

卧槽?源码里是空的?

 

当然了,这个handleMessage()方法就是这个"包裹"Message被丢来丢去后最后要被进行处理的地方(被丢给Runable处理的不算),你当然要重写这个方法,给出你自己的处理方法了。

比如,把我上面写的那个解包提取信息的语句写进去......

 

一句话总结:

Handler是整个工作流水线的"传递者"和Message"包裹"的"分发者"(比如甩给Runnable)、"处理者",是流水线的"工人"。

 

好像,都写完了?

其实并没有,似乎,我们还有个Handler的post()方法没说。这个方法,就是线程把"包裹"丢给Handler,让Handler送其入列的方法。

4.Handler.post()

这个post到底有哪些方法呢?

public final boolean sendMessage(Message msg);//丢包法1,不吵吵直接丢
public final boolean sendEmptyMessageDelayed(int what, long delayMillis);//丢包法2,延迟一段时间后丢一个空包,只含一个空的数据what=0,告诉Looper:“嘿哥们你还没空,继续转~”
public final boolean sendMessageDelayed(Message msg, long delayMillis);//丢包法3,延迟一段时间后丢
public boolean sendMessageAtTime(Message msg, long uptimeMillis);//丢包法4,在指定的时间丢

下面我会给出他们的源码,当然你要没啥兴趣读,也没关系,你可以直接看我给的结论,那就是

 

丢包法1中其实最后调用了丢包法2,2中调3,3中调4,4中调用了enqueueMessage(queue, msg, uptimeMillis),终于把这个包丢进了流水线"履带"MessageQueue。在enqueueMessage()方法中,会进行msg.target=this的操作,也就是我们刚刚说的"盖章"。

源码:

public final boolean sendMessage(Message msg){  
	return sendMessageDelayed(msg, 0);  
}  
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
    Message msg = Message.obtain();  
    msg.what = what;  
    return sendMessageDelayed(msg, delayMillis);  
}  
public final boolean sendMessageDelayed(Message msg, long delayMillis){  
	if (delayMillis < 0) {  
		delayMillis = 0;  
	}  
	return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
}  
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
	MessageQueue queue = mQueue;  
	if (queue == null) {  
		RuntimeException e = new RuntimeException(  
                   this + " sendMessageAtTime() called with no mQueue");  
		Log.w("Looper", e.getMessage(), e);  
		return false;  
        }  
	return enqueueMessage(queue, msg, uptimeMillis);  
}  

这就是线程把信息"包裹"Message靠"工人"Message送入列的方法了。


别急,别忘了,handler其实还有个"分发者"的身份,把信息丢给Runnable处理的方法把?那么是怎么做的呢?其实一个Handler还有这个方法:

public final boolean post(Runnable r)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)

是的,其实也就是把mCallback的值变成这个Runnable而已罢了...

 

你可能要问,我一个好好的Runnable,怎么到了你Handler就变成一个Message了呢?

其实吧,Hander内部提供了getPostMessage方法把Runnable对象转化为Message:

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

 

好的,到了这里,我们把这个利用Looper、MessageQueue、Handler进行消息传递的流程总结一下:

一个线程,首先进行Looper.prepare(),就可以创建出一个绑定了MessageQueue"履带"的[唯一]的Looper+MessageQueue"流水线";然后线程可以实例化出几个Handler"工人"。线程有要处理的信息"包裹"Message了,丢给对应的Handler"工人";这个工人判断一下这个到底是个Runnable还是Message,如果是Runnable就包装成一个Message,再"盖章",然后丢向流水线,让它排队;不过不是,"盖完章"不多bb直接甩进流水线。一个Message"包裹"到了流水线的队首,就要被拿出来,根据刚刚盖的章,各找各妈各回各家,该上哪上哪,然后进行msg.target.diapatchMessage()->msg.target.handleMessage()拆包处理。

看完这个总结,再去看我的图,是不是理解了?

恩,其实还有一点:你看我给的图的右边,还有个Thread 2,里面也站了一个Handler"工人",它负责把Thread 2要发给Thread 1的包裹丢进Thread 1的流水线。在编制上,他是Thread 1的"工人"(在Thread 1中实例化)。一般来说,Thread 2其实是 Thread 1的子线程。

为什么说是子线程呢?要是Thread 1 在Thread 2 之前结束了,这名"工人"就被内存杀掉了,包要丢给谁?如果是子线程就放心,要么一起死,要么Thread 2死在 Thread 1之前。


 

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值