[图解法结合源码]理解、记忆Handler、Looper、MessageQueue之间的关系
2017-04-17 19:19
543 查看
>[图解法结合源码理解、记忆Handler、Looper、MessageQueue之间的关系]
看了不少关于Handler、Looper、MessageQueue之间关系的文章。感觉挺枯燥的,上来就是一团代码,看着心烦。
后来我捋了捋,画了个图。先看图,我们再来谈他们间的关系:
在这个图中,我做了个类比:(很重要,多看几遍)
MessageQueue,流水线上的"履带";
Looper,履带的"驱动轮";
Handler,流水线上的"工人";
Message,流水线上的"包裹"。
现在让我们来解释这三者间的工作关系,首先,我们要明确一个基本法:
一个Thread只能有且只能一个Looper。一个Looper只能对应一个Message。一个Looper和MessageQueue的绑定体可以对应多个Handler。(参考上图)
1.Looper与MessageQueue
刚刚说了一个Thread只能有一个Looper。为什么只能有一个呢?让我们去看它的一个方法Looper.prepare()方法的源码:
然后我们再来说说一个线程一般要怎么创建一个Looper,例子A:(下面会拿这个例子讲解)
首先,一个线程先Looper.prepare(),创建一个Looper,在这个prepare()方法中,回头看一眼基本法里的代码,它会在 return 中new 一个(Looper(true));其实,就是下面的代码:
现在驱动轮有了,履带有了,要让这个流水线动起来,显然,还需要另外一个操作。
没错,这个操作就是例子A中倒二行的Looper.loop()。其源码为:(仅列出你需要理解的代码,其他你不需要关心)
上面的loop()中我给出了详细的注解,你稍微花两分钟就能看明白。第8行,从MessageQueue队列中取走第一个消息,然后在第13行,调用msg.target的disspachMessage()方法,分发这个消息。其实msg.target就是个handler,至于为什么,我会在Handler的部分进行讲解,你先这个记着。
Looper部分总结一句话:
在线程的开头,为这个线程准备一个Looper(Looper.prepare()),这个Looper会自动绑定上一个MessageQueue,然后在线程的最后,通过Looper.loop()方法,让这个MessageQueue转动起来,传送这个MessageQueue上的消息对象Message。每次[b]Message被传送到队首,这个Message会被disspatchMessage()方法分发出去,分配到管理它的Handler(流水线工人)那里进行处理。[/b]
2.Message
我们在图中做了一个类比,把Message当成了一个包裹,那么它里面到底包裹了啥?我们来看一下它的源码:
一个handler通过post方法(我们下面再说,总是就是线程把这个消息Message丢给了Handler)拿到一个Message之后,这个Handler并不是马上处理这个消息,而是先盖上一个章:
有的同学可能会问了,Handler拿到Message为什么不马上处理呢?
原因是:本身Handler并没有提供并发解决机制,但MessageQueue的next()提供了并发解决机制。稍微理解一下这句话,很容易理解的。要知道Handler不只能接收到本线程丢过来的Msg包,还能接到其他线程(一般是子线程)丢过来的包(参考第一幅图),你不搞个基本法,排队来解决,这个程序怕是药丸。
这里还要注意一下那个Runnable callback的回调。现在先别管,总之先记着,我挖的坑我肯定会填的。
Message要注意的只有两种用法,一个是把信息封装,一个是解包提取信息;
一句话总结,Message就是个"包裹",里面装了你需要传递信息,然后被丢来丢去(╯‵□′)╯︵,最后被分配到它的工人那里拆包进行处理。
3.Handler
终于,这篇文章要写完了。QAQ
接下来,我们要说我们的主角了——Handler了。大部分时候,你不需要关心MessageQueue和Looper是怎么工作的,但是Handler是你时时刻刻都必须打招呼的家伙。
首先是Handler究竟是个什么家伙。我把它类比为流水线上"工人" ,它即负责把收到的消息入列,也负责处理队首属于自己的那一份Message。
它的构造函数里没有啥东西,你只需要知道,它会先获取当前线程的Looper并绑定,然后从这个Looper获取到它的MessageQueue。然后,Handler与Looper、MessageQueue的关系就建立起来了。另外,他还创建了一个mCallback。这个待会儿再说。
刚刚我们一直在说msg.target.dispatchMessage()方法(其实也就是msg绑定的handler的disspatchMessage()),那么这个方法到底是个啥东西呢?看源码:
这一整段的代码就是告诉你:要分发这个代码,先看这个包裹的callback有没有绑定上啥东西(一个Runable);如果有,直接交由这个Runable进行处理,如果没有,查看当前handler的mCallback有没有绑定上啥东西;如果有,交由这个mCallback处理,如果没有,那么就调用当前handler的handleMessage()方法处理。
你可能要问,这个mCallback到底是个啥玩意?其实就是这个玩意:
现在要说的是handleMessage()方法,它的源码:
当然了,这个handleMessage()方法就是这个"包裹"Message被丢来丢去后最后要被进行处理的地方(被丢给Runable处理的不算),你当然要重写这个方法,给出你自己的处理方法了。
比如,把我上面写的那个解包提取信息的语句写进去......
一句话总结:
Handler是整个工作流水线的"传递者"和Message"包裹"的"分发者"(比如甩给Runnable)、"处理者",是流水线的"工人们"。
好像,都写完了?
其实并没有,似乎,我们还有个Handler的post()方法没说。这个方法,就是线程把"包裹"丢给Handler,让Handler送其入列的方法。
4.Handler.post()
这个post到底有哪些方法呢?
丢包法1中其实最后调用了丢包法2,2中调3,3中调4,4中调用了enqueueMessage(queue, msg, uptimeMillis),终于把这个包丢进了流水线"履带"MessageQueue。在enqueueMessage()方法中,会进行msg.target=this的操作,也就是我们刚刚说的"盖章"。
源码:
别急,别忘了,handler其实还有个"分发者"的身份,把信息丢给Runnable处理的方法把?那么是怎么做的呢?其实一个Handler还有这个方法:
你可能要问,我一个好好的Runnable,怎么到了你Handler就变成一个Message了呢?
其实吧,Hander内部提供了getPostMessage方法把Runnable对象转化为Message:
好的,到了这里,我们把这个利用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之前。
如果没看懂,留个言?
看了不少关于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。每次[b]Message被传送到队首,这个Message会被disspatchMessage()方法分发出去,分配到管理它的Handler(流水线工人)那里进行处理。[/b]
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
终于,这篇文章要写完了。QAQ
接下来,我们要说我们的主角了——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之前。
如果没看懂,留个言?
相关文章推荐
- 手把手带你从源码的角度全面理解Handler、Looper、MessageQueue之间的关系
- Android 阅读源码,让你彻底理解Handler、Message、Looper之间的关系
- Android中的Thread,MessageQueue,Looper,Message,Handler之间的关系图解
- 深入理解Looper、MessageQueue、Handler之间的关系,Android的异步消息机制
- 从源码中分析Handler, Looper, Message, MessageQueue之间的关系
- 从HandlerThread源码理清handler、looper与messageQueue之间的关系
- android的消息处理机制(图+源码分析)——Thread,Looper,MessageQueue,Message,Handler之间的关系
- 从Android源码角度对Handler,MessageQueue,Looper之间消息传递工作原理的理解
- android开发步步为营之79:通过源码分析Looper,Handler,MessageQueue之间的关系
- 解析Handler,MessageQueue,Message,Looper之间的关系
- Handler,Looper,Message,MessageQueue之间关系浅析
- Thread,Looper,Handler,Message,MessageQueue之间的关系
- 正确理解 AsyncTask,Looper,Handler三者之间的关系(基于android 4.0)
- Android笔记-MultiThreading in Android(1)-Thread,Looper,Handler,Message,MessageQueue之间的关系
- 理解Handler、Looper、MessageQueue、Thread关系?
- Message、Handler、MessageQueue、Looper之间关系图文总结
- 单线程模型中Message、Handler、MessageQueue、Looper之间的关系
- Android笔记-MultiThreading in Android(1)-Thread,Looper,Handler,Message,MessageQueue之间的关系
- 彻底搞定Looper,Handler,Message,MessageQueue,和Thread之间的关系
- Android笔记-MultiThreading in Android(1)-Thread,Looper,Handler,Message,MessageQueue之间的关系