Handler源码解析系列一

版权声明 版权声明:本文为博主原创文章,转载请注明出处+地址
什么是Handler消息机制 Android内部的Handler消息机制,是将子线程处理的结果,通过Handler异步回调给主线程,让主线程去进行操作的一个机制。
Handler消息机制的工作流程图 Handler源码解析系列一
文章图片
Handler调用流程图.png 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取出,返回给调用方
【Handler源码解析系列一】那么以上就是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源码的一个简单的分析,后续会对一些细节化的设计,进行专题讲解,敬请关注。

    推荐阅读