Android 系统(18)---Handler,MessageQueue与Looper关系
一说到Android的消息机制,自然就会联想到Handler,我们知道Handler是Android消息机制的上层接口,因此我们在开发过程中也只需要和Handler交互即可,很多人认为Handler的作用就是更新UI,这也确实没错,但除了更新UI,Handler其实还有很多其他用途,比如我们需要在子线程进行耗时的I/O操作,可能是读取某些文件或者去访问网络等,当耗时操作完成后我们可能需要在UI上做出相应的改变,但由于Android系统的限制,我们是不能在子线程更新UI控件的,否则就会报异常,这个时候Handler就可以派上用场了,我们可以通过Handler切换到主线程中执行UI更新操作。
1.Handler和Looper
当Handler实例化的时候,Looper会自动关联Handler所在线程,如果Looper什么都不设置,默认关联的就是主线程
2.Looper和MessageQueue
当Looper实例化的时候,内部会初始化MessageQueue消息队列,消息队列如果和主线程关联,是打开的;如果不是主线程,要自己操作(消息队列打开就能接受消息,不打开就不接受消息)。
有专门控制的标志变量
3.Handler和Message
可以对Message操作,发送,删除
4.Handler和MessageQueue
Handler发送的消息由MessageQueue统一处理,MessageQueue通过回调方法可以确认该消息已经处理过了
是通过回调接口用的回调方法
5.Message和MessageQueue
MessageQueue里面存放Message
下面是Handler一些常用方法:
void handleMessage(Message msg):处理消息的方法,该方法通常会被重写。
final boolean hasMessages(int what):检测消息队列中是否包含what属性为指定值的消息。
Message obtainMessage():获取消息的方法,此函数有多个重载方法。
sendEmptyMessage(int what):发送空消息。
final boolean sendEmptyMessageDelayed(int what , long delayMillis):指定多少毫秒后发送空消息。
final boolean sendMessage(Message msg):立即发送消息。
final boolean sendMessageDelayed(Message msg ,long delayMillis):指定多少毫秒后发送消息。
final boolean post(Runnable r):执行runnable操作。
final boolean postAtTime(Runnable r, long upTimeMillis):在指定时间执行runnable操作。
final boolean postDelayed(Runnable r, long delayMillis):指定多少毫秒后执行runnable操作。
介绍完方法后,我们就从一个简单的例子入手吧,然后一步步的分析:
- public class MainActivity extends AppCompatActivity {
- public static final int MSG_FINISH = 0X001;
- //创建一个Handler的匿名内部类
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_FINISH:
- LogUtils.e("handler所在的线程id是-->" + Thread.currentThread().getName());
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- //启动耗时操作
- consumeTimeThread(findViewById(R.id.tv));
- // handler.post()
- }
- //启动一个耗时线程
- public void consumeTimeThread(View view) {
- new Thread() {
- public void run() {
- try {
- LogUtils.e("耗时子线程的Name是--->" + Thread.currentThread().getName());
- //在子线程运行
- Thread.sleep(2000);
- //完成后,发送下载完成消息
- handler.sendEmptyMessage(MSG_FINISH);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }.start();
- }
- }
运行结果:
上面的例子其实就是Handler的基本使用,在主线中创建了一个Handler对象,然后通过在子线程中模拟一个耗时操作完成后通过sendEmptyMessage(int)方法发送一个消息通知主线程的Handler去执行相应的操作。通过运行结果我们也可以知道Handler确实也是在主线程运行的。
那么问题来了,通过Handler发送的消息是怎么到达主线程的呢?接下来我们就来掰掰其中的奥妙,前方高能,请集中注意力!为了更好的理解Handler的工作原理,我们先来介绍与Handler一起工作的几个组件:
Message:Handler接收和处理消息的对象。
Looper:每个线程只能有一个Looper。它的loop方法负责读取MessageQueue中的消息,读到消息后把消息发送给Handler进行处理。
MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时,会在它的构造方法中创建MessageQueue对象。
Handler:它的作用有两个—发送消息和处理消息,程序使用Handler发送消息,由Handler发送的消息必须被送到指定的MessageQueue;否则消息就没有在MessageQueue进行保存了。而MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作的话,就必须在当前线程中有一个Looper对象。我们先对上面的几个组件有大概的了解就好,后面我们都会详细分析,既然消息是从Handler发送出去,那么我们就先从Handler入手吧。先来看看Handler 的构造方法源码:
- public class Handler {
- /**
- * 未实现的空方法handleMessage()
- */
- public void handleMessage(Message msg) {
- }
- /**
- * 我们通常用于创建Handler的构造方法之一
- */
- public Handler() {
- this(null, false);
- }
- // 构造方法的内调用的this(null, false)的具体实现
- public Handler(Callback callback, boolean async) {
- //检查Handler是否是static的,如果不是的,那么有可能导致内存泄露
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- //重要的组件出现啦!Looper我们先理解成一个消息队列的管理者,用来从消息队列中取消息的,后续会详细分析
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- //这个异常很熟悉吧,Handler是必须在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler
- //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- //将mLooper里面的消息队列复制到自身的mQueue,这也就意味着Handler和Looper是公用一个消息队列
- mQueue = mLooper.mQueue;
- //回调函数默认是Null
- mCallback = null;
- }
分析:Handler的构造方法源码不是很多,也比较简单,但是我们从源码中也可以得知,在创建Handler时,Handler内部会去创建一个Looper对象,这个Looper对象是通过Looper.myLooper()创建的(后续会分析这个方法),同时还会创建一个消息队列MessageQueue,而这个MessageQueue是从Looper中获取的,这也就意味着Handler和Looper共用一个消息队列,当然此时Handler,Looper以及MessageQueue已经捆绑到一起了。上面还有一个情况要说明的,那就是:
- if (mLooper == null) {
- //这个异常很熟悉吧,Handler是必须在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler
- //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- } </span>
这里先回去判断Looper是否为空,如果为null,那么就会报错,这个错误对我们来说应该比较熟悉吧,那为什么会报这个错误呢?我们在前面说过Handler的作用有两个—发送消息和处理消息,我们在使用Handler发送消息,由Handler发送的消息必须被送到指定的MessageQueue;否则就无法进行消息循环。而MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作的话,就必须在当前线程中有一个Looper对象。那么又该如何保障当前线程中一定有Looper对象呢?这里其实分两种情况:
(1)在主UI线程中,系统已经初始化好了一个Looper对象,因此我们可以直接创建Handler并使用即可。
(2)在子线程中,我们就必须自己手动去创建一个Looper对象,并且去启动它,才可以使用Handler进行消息发送与处理。使用事例如下:
- class childThread extends Thread{
- public Handler mHandler;
- @Override
- public void run() {
- //子线程中必须先创建Looper
- Looper.prepare();
- mHandler =new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- //处理消息
- }
- };
- //启动looper循环
- Looper.loop();
- }
- }
分析完Handler的构造方法,我们接着看看通过Handler发送的消息到底是发送到哪里了?我们先来看看Handler的几个主要方法源码:
- // 发送一个空消息的方法,实际上添加到MessagerQueue队列中
- public final boolean sendEmptyMessage(int what) {
- return sendEmptyMessageDelayed(what, 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);
- }
- // 最后调用此方法添加到消息队列中
- private boolean enqueueMessage(MessageQueue queue, Message msg,
- long uptimeMillis) {
- msg.target = this;// 设置发送目标对象是Handler本身
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息队列中
- }
- // 在looper类中的loop()方法内部调用的方法
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
分析:通过源码我们可以知道,当我们调用sendEmptyMessage(int)发送消息后。最终Handler内部会去调用enqueueMessage(MessageQueue queue,Message msg)方法把发送的消息添加到消息队列MessageQueue中,同时还有设置msg.target=this此时就把当前handler对象绑定到msg.target中了,这样就完成了Handler向消息队列存放消息的过程。这个还有一个要注意的方法 dispatchMessage(Message),这个方法最终会在looper中被调用(这里我们先知道这点就行,后续还会分析)。话说我们一直在说MessageQueue消息队列,但这个消息队列到底是什么啊?其实在Android中的消息队列指的也是MessageQueue,MessageQueue主要包含了两种操作,插入和读取,而读取操作本身也会伴随着删除操作,插入和读取对应的分别是enqueueMessage和next,其中enqueueMessage是向消息队列中插入一条消息,而next的作用则是从消息队列中取出一条消息并将其从队列中删除。虽然我们一直称其为消息队列但是它的内部实现并不是队列,而是通过一个单链表的数据结构来维护消息列表的,因为我们知道单链表在插入和删除上比较有优势。至内MessageQueue的内部实现,这个属于数据结构的范畴,我们就不过多讨论了,还是回到原来的主题上来,到这里我们都知道Handler发送的消息最终会添加到MessageQueue中,但到达MessageQueue后消息又是如何处理的呢?还记得我们前面说过MessageQueue是由Looper负责管理的吧,现在我们就来看看Looper到底是如何管理MessageQueue的?
- public final class Looper {
- // sThreadLocal.get() will return null unless you've called prepare().
- //存放线程的容器类,为确保获取的线程和原来的一样
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
- private static Looper sMainLooper; // guarded by Looper.class
- //消息队列
- final MessageQueue mQueue;
- final Thread mThread;
- //perpare()方法,用来初始化一个Looper对象
- public static void prepare() {
- prepare(true);
- }
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
- //handler调用的获取Looper对象的方法。实际是在ThreadLocal中获取。
- public static Looper myLooper() {
- return sThreadLocal.get();
- }
- //Looper类的构造方法,可以发现创建Looper的同时也创建了消息队列MessageQueue对象
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
- //这个方法是给系统调用的,UI线程通过调用这个线程,从而保证UI线程里有一个Looper
- //需要注意:如果一个线程是UI线程,那么myLooper和getMainLooper是同一个Looper
- public static final void prepareMainLooper() {
- prepare();
- setMainLooper(myLooper());
- if (Process.supportsProcesses()) {
- myLooper().mQueue.mQuitAllowed = false;
- }
- }
- //获得UI线程的Looper,通常我们想Hanlder的handleMessage在UI线程执行时通常会new Handler(getMainLooper());
- public synchronized static final Looper getMainLooper() {
- return mMainLooper;
- }
- //looper中最重要的方法loop(),该方法是个死循环,会不断去消息队列MessageQueue中获取消息,然后调dispatchMessage(msg)方法去执行
- public static void loop() {
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- final MessageQueue queue = me.mQueue;
- //死循环
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- //这里其实就是调用handler中的方法,而在Handler的源码中也可以知道dispatchMessage(msg)内部调用的就是handlerMessage()方法
- msg.target.dispatchMessage(msg);
- msg.recycle();
- }
- }
分析:代码不算多,我们拆分开慢慢说,在Looper源码中我们可以得知其内部是通过一个ThreadLocal的容器来存放Looper的对象本身的,这样就可以确保每个线程获取到的looper都是唯一的。那么Looper对象是如何被创建的呢?通过源码我们可以知道perpare()方法就可以创建Looper对象:
- //perpare()方法,用来初始化一个Looper对象
- public static void prepare() {
- prepare(true);
- }
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
在创建Looper对象前先会去判断ThreadLocal中是否已经存在Looper对象,如果不存在就新创建一个Looper对象并且存放ThreadLocal中。这里还有一个要注意的是在Looper创建的同时MessageQueue消息队列也被创建完成,这样的话Looper中就持有了MessageQueue对象。
- //Looper类的构造方法,可以发现创建Looper的同时也创建了消息队列MessageQueue对象
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
那么我们如何获取已经创建好的Looper对象呢?通过源码我们知道myLooper()方法就可以获取到Looper对象:
- //handler调用的获取Looper对象的方法。实际是在ThreadLocal中获取。
- public static Looper myLooper() {
- return sThreadLocal.get();
- }
Looper对象的创建和获取,还有MessageQueue对象的创建,现在我们都很清楚了,但是Looper到底是怎么管理MessageQueue对象的呢?这就要看looper()方法了:
- //looper中最重要的方法loop(),该方法是个死循环,
- //会不断去消息队列MessageQueue中获取消息,
- //然后调dispatchMessage(msg)方法去执行
- public static void loop() {
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- final MessageQueue queue = me.mQueue;
- //死循环
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- //这里其实就是调用handler中的方法,而在Handler的源码中也可以知道dispatchMessage(msg)内部调用的就是handlerMessage()方法
- msg.target.dispatchMessage(msg);
- msg.recycle();
- }
通过looper()方法内部源码我们可以知道,首先会通过myLoooper()去获取一个Looper对象,如果Looper对象为null,就会报出一个我们非常熟悉的错误提示,“No Looper;Looper.prepare() wasn't called on this thread”,要求我们先通过Looper.prepare()方法去创建Looper对象;如果Looper不为null,那么就会去获取消息队列MessageQueue对象,接着就进入一个for的死循环,不断从消息队列MessageQueue对象中获取消息,如果消息不为空,那么久会调用msg.target的dispatchMessage(Message)方法,那么这个target又是什么,没错target就是我们创建的Handler对象,还记得我们前面分析Handler源码时说过的那个方法嘛?
- // 在looper类中的loop()方法内部调用的方法
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
现在明白了吧?首先,检测Message的callback是否为null,不为null就通过handleCallback方法来处理消息,那么Message的callback是什么?其实就是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数,我们顺便看看post方法源码:
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
getPostMessage(Runnable r)方法源码:
- private static Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
现在明白Message的callback是什么了吧?而对应handleCallback方法逻辑也比较简单:
- private static void handleCallback(Message message) {
- message.callback.run();
- }
嗯,是的,因此最终执行的还是通过post方法传递进来的Runnable参数的run方法。好了,我们继续dispatchMessage()方法的分析,接着会去检查mCallback是否为null,不为null,则调用mCallback的handleMessage方法来处理消息。至于Callback则就是一个接口定义如下:
- /**
- * Callback interface you can use when instantiating a Handler to avoid
- * having to implement your own subclass of Handler.
- *
- * @param msg A {@link android.os.Message Message} object
- * @return True if no further handling is desired
- */
- public interface Callback {
- public boolean handleMessage(Message msg);
- }
这个接口有什么用呢?其实通过Callback接口我们就可以采取如下方法来创建Handler对象:
- Handler handler =new Handler(callback)
那么这样做到底有什么意义,其实这样做可以用callback来创建一个Handler的实例而无需派生Handler的子类。在我们的开发过程中,我们经常使用的方法就是派生一个Hanlder子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种方式,那就是当我们不想派生子类的时候,可以通过Callback来实现。 继续dispatchMessage()方法的分析,最后如果以上条件都不成立的话,就会去调用Handler的handleMessage方法来处理消息。而 我们的Handler是在主线程创建的,也就是说Looper也是主线程的Looper,因此handleMessage内部处理最终都会在主线程上执行,就这样整个流程都执行完了。下面提供一个图解帮助大家理解:
最后我们来个小总结:Android中的Looper类主要作用是来封装消息循环和消息队列的,用于在android线程中进行消息处理。handler是用来向消息队列中插入消息的并最好对消息进行处理。
看了不少关于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
终于,这篇文章要写完了。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之前。
Android 系统(18)---Handler,MessageQueue与Looper关系相关推荐
- 【Framework】透视Android中的Handler
准备对基于Android应用开发Framework层的内容进行学习回顾,学习一个新技术前我们一般都会灵魂三问:What-Why-How(是什么.为什么.怎么用).源码的学习一定要亲自去看,用IDE或者 ...
- android版本内存多少,内存大小决定Android系统版本?
随着Android系统版本的更新(从Android 1.x-4.x),Android手机的内存容量也在不断的翻番中.从最早的256MB到最新的3GB,12倍的数值变化在彰显科技的进步之余,也潜移默化地 ...
- Android系统开发之五:多线程编程详解(Handler ,Looper , Message , MessageQueue)
本期的多线程主题与Android相关,侧重讲解在Android中如何用好多线程,需要你有Java的多线程基础. 首先我们思考几个问题,在Android应用中为什么要用多线程?为了解决哪些问题?或者为了 ...
- Android中的Handler, Looper, MessageQueue和Thread
前几天,和同事探讨了一下Android中的消息机制,探究了消息的发送和接收过程以及与线程之间的关系.虽然我们经常使用这些基础的东西,但对于其内部原理的了解,能使我们更加容易.合理地架构系统,并避免一些 ...
- 正确理解 AsyncTask,Looper,Handler三者之间的关系(基于android 4.0)
Looper 和Handler 是理解好AsyncTask的一个基础,我们可以先从这里开始,先给出一个主线程和子线程互相通信的例子. 1 package com.example.loopertest; ...
- Android消息机制(Handler、MessageQueue、Looper)详细介绍
Android的消息机制其实在android的开发过程中指的也就是Handler的运行机制,这也就引出了android中常见的面试问题: 简述Handler.Looper.MessageQueue的含 ...
- Android 系统(59)---Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)
Android开发:Handler异步通信机制全面解析(包含Looper.Message Queue) 前言 最近刚好在做关于异步通信的需求,那么,今天我们来讲解下Android开发中的Handler ...
- Android异步消息处理机制 深入理解Looper、Handler、Message三者关系
转载子:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中 ...
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
很多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源码角度介绍3者关系,然后给出一个容易记忆的结论. 1. 概述 ...
最新文章
- html js更改title,如何使用js改变HTML中title里面固定的文字
- 十大经典排序算法之冒泡排序及其优化
- 构造函数和clone以及在继承中
- 你可能被网帖骗了:中国粗离婚率的15连涨已在2018年被终结了
- Postgresql的一些命令
- jQuery UI 实现 仿购物车功能 简洁的js
- FTP 简介与 Windows 系统搭建 FTP 服务器
- SpringMVC一路总结(一)
- 管理感悟:出了事故,关键是想想自己哪里能改进
- oracle10g rac导出ocr,Oracle RAC OCR磁盘故障快速恢复方法
- Java线程并发协作与任务定时调度
- 计算机组成原理复习要点与考题类型--选择-填空-分析-计算-简答
- office 文档 在线查看
- rockchip研讨会_地下在线研讨会6
- ISO、光圈、曝光、焦距
- MDD | TO-252封装选型指南
- 无线定位技术实验一 TDOA-FDOA联合定位
- 参加全国大学生智能汽车竞赛,快来申请沁恒RISC-V MCU!
- 一步步教你做“锅打灰太狼”
- 过夫妻生活:50岁男人比30岁时更有魅力