什么是Handler消息机制

Android内部的Handler消息机制,是将子线程处理的结果,通过Handler异步回调给主线程,让主线程去进行操作的一个机制。

Handler消息机制的工作流程图

Handler源码解析系列一

Handler的源码解析

Handler使用案例

首先,给大家简单的展示下Handler的使用流程代码

/**
 * Function:简述Handler使用方式
 *
 * @author wanzi Created on 2019/4/28
 */
public class HandlerActivity extends AppCompatActivity {
    private MyHandler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mHandler = new MyHandler();
        Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 1;
                mHandler.sendMessage(msg);
            }
        }, 5000, TimeUnit.MILLISECONDS);
    }

    private static class MyHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                Log.d("HandlerTestActivity", "Handler发来贺电");
            }
        }
    }
}

Handler是如何将消息加入到消息队列?

分析源码,我们首先要找到切入点,mHandler.sendMessage(msg),代码从这个函数开始调用的handler,那么继续往下追踪,我们来研究下,在发送消息之后,Handler干了什么?
Handler.java

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
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);
    }

在经过层层调用之后,我们发现调用到了enqueueMessage方法,根据字面意思,就是加入消息队列,那我们继续看里面的实现
Handler.java

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
  • 将当前的handler对象赋值给了msg.target
  • 调用了messagequeue的enqueueMessage方法
    MessageQueue.class
 boolean enqueueMessage(Message msg, long when) {
       ...

        synchronized (this) {
         ...
            if (p == null || when == 0 || when < p.when) {
           ...
            } else {
                ...
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

          ...
        }
        return true;
    }

到此为止,msg算是终于成功的加入到消息队列当中,当然MessageQueue.enqueueMessage的内容远不止这么简单,里面包含一些异常处理,唤醒机制等等,单独写一篇文章详细的讲解。

Handler如何从消息队列获取消息?

主线程的Looper如何创建?

说到获取消息,我们还得从Looper说起,大家都知道,在主线程中创建的Handler对象,是自带Looper对象的,那么主线程中的Looper是从哪里创建的呢?
我们知道,程序启动开始的入口在ActivityThread.class的main函数,我们来看看代码
ActivityThread.class

public static void main(String[] args) {
      ...
        Looper.prepareMainLooper();

       ...
        Looper.loop();

        ...
    }

简化之后,我们可以看出,在main函数中,有两处和Looper相关的代码,那么我们分别来看下
** Looper.class**

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

 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));
    }
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
  • prepareMainLooper最终调用到的是prepare函数
  • prepare的quitAllowed参数值为false,意味着在主线程中,不允许退出
  • 在prepare函数中,我们new了一个Looper对象,放在了一个ThreadLocal的对象中保存
  • sMainLooper 是将sThreadLocal中保存的值取出,赋值给它

ThreadLocal的简单介绍

在整个Looper的创建以及获取的过程,其实主要跟ThreadLocal的set,get函数相关,那么我们就来看看这两个函数的源码

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  • set函数虽然只需要传value值进来,但是其实它是获取的当前线程作为key值,将其保存,这也就说明为什么一个线程只对应来一个looper,因为他的存储的数据结构只支持一对一的关系。
  • get函数也是通过以当前线程作为key将对应的entry取出,返回给调用方

那么以上就是mainLooper的创建的主要流程

主线程的Looper如何开启消息轮询的呢?

大家回顾下前面ActivityThread.main方法,我们还有一行代码没有分析,那就是Looper.loop,根据字面意思我们也能猜出,这是开始进行循环,话不多说,上代码
Looper.class

 public static void loop() {
        final Looper me = myLooper();
    ...
        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;
            }
    ...
    ...
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
          ...
    }

这里我们同样省略了很多源码,只分析主要流程

  • 通过myLooper()获取到当前线程的looper对象,上面也带大家一起分析过源码了,有不清楚的可以回过去看看
  • 通过拿到的looper对象去获取到当前的消息队列
  • 进入死循环
    • 不断的从队列中获取新的消息
    • 获取到消息后,msg.target.dispatchMessage(msg)

以上就是Looper开启轮询的整个调用流程。

下面我带大家一起看下几个问题:

  1. MessageQueue.next是如何拿到message的呢?
    MessgeQueue.class
  Message next() {
      ...
        for (;;) {
           ...
            synchronized (this) {
              ...
                Message prevMsg = null;
                Message msg = mMessages;
                ...
                if (msg != null) {
                    if (now < msg.when) {
                       ...
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                       ...
                        msg.markInUse();
                        return msg;
                    }
                } else {
                  ...
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

             ...
    }
  • 在next函数中也有一个死循环,当获取到我们的msg之后,跳出循环
  • next函数,就是从当前的message获取到nextMessage,并将其返回给looper

看完之后,你一定还有很多疑问,为什么要用到死循环?当消息队列为空的时候,next函数会怎么办?等等等等,跟MessageQueue.enqueueMessgae方法一样,next函数的实现大有文章,单独为大家写一篇详细介绍,这里只作简单的流程分析。

  1. 什么情况下msg为空,跳出整个循环呢?
    这一块先跟大家简单的介绍一下,在next函数中,我们发现有return 为 null的情况,取决于一个叫mQuitting的参数,其实就是调用了MessageQueue.quit()方法,将其设置为true。但是在主线程中,大家还记得我们在Looper.prepare传入的false参数吗?它不允许主线程的MessageQueue执行quit方法,直接抛出异常。
 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
  1. msg.target.dispatchMessage(msg)是怎么回调到handler.handleMessage方法的呢
    ** Message.class **
   /*package*/ Handler target;

是的,正如上面我们所预料的,target其实是一个handler的对象,在Handler.enqueueMessage方法中,我们将msg与handler进行了关联,那么在Looper.loop里面,msg.target.dispatchMessage(msg),根据源码可以得知,我们将其回调给了handleMessage进行事件处理。

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

以上就是针对Handler源码的一个简单的分析,后续会对一些细节化的设计,进行专题讲解,敬请关注。

分类:

技术点:

相关文章: