这也是
Android
中老生常谈的一个话题了,它本身并不是很复杂,可是面试官比较喜欢问。本文就从源码再简单的理一下这个机制。也可以说是理一下Handler
、Looper
、MessageQueue
之间的关系。
单线程中的消息处理机制的实现
首先我们以Looper.java
源码中给出的一个例子来分析一下在单线程中如何使用这个机制:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
Looper.prepare()
上面只涉及到Handler
和Looper
,MessageQueue
呢?我们来看一下Looper.prepare()
:
private static void prepare(boolean quitAllowed) { ... sThreadLocal.set(new Looper(quitAllowed)); }
很简单,即new Looper
,然后把它放到ThreadLocal<Looper>
中(ThreadLocal<Looper>保存在线程的私有map中)。继续看一下Looper的构造方法
:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); ... }
即,在这里MessageQueue
作为Looper
的成员变量被初始化了。所以 一个Looper对应一个MessageQueue 。 ok,到这里Looper.prepare()
所涉及的逻辑以及浏览完毕,继续看一下new Handler()
:
new Handler()
mLooper = Looper.myLooper();if (mLooper == null) { throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;
Handler
会持有一个Looper
, 那我们看一下这个Looper
来自于哪里: Looper.myLooper()
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
即会从当前线程的私有map中取出ThreadLocal<Looper>
。所以Handler
默认持有当前线程的Looper
的引用。如果当前线程没有Looper
,那么Handler
就会构造失败,抛出异常。其实可以在构造Handler
时指定一个Looper
,下面会讲到这个。
在持有当前线程的Looper
的引用同时,Handler
在构造时也会获取Looper
的成员变量MessageQueue
,并持有它。 如果你在一个线程中同时new多个Handler
的话,那他们的关系如下图所示:
Looper_Handler_MessageQueue.png
即:
Looper
和MessageQueue
存放在当前线程的ThreadLocal
中Handler
持有当前线程的Looper
和MessageQueue
。
Looper.loop()
这个方法可以说是核心了:
public static void loop( ) { final Looper me = myLooper(); //... final MessageQueue queue = me.mQueue; //... for (;;) { Message msg = queue.next(); // might block //… msg.target.dispatchMessage(msg); //... } }
即Looper
不断检查MessageQueue
中是否有消息Message
,并调用msg.target.dispatchMessage(msg)
处理这个消息。 那msg.target
是什么呢?其实它是Handler
的引用。
msg.target.dispatchMessage(msg)
会导致Handler.handleMessage()
的调用,其实到这里单线程中的消息处理机制模型
已经有了一个大致的轮廓了,接下来就需要弄清楚
msg.target
是在哪里赋值的?消息是如何插入到
MessageQueue
中的?
发送一个消息
以Handler
发送一个最简单的消息为例:
handler.sendEmptyMessage(0)
这个方法最终调用到的核心逻辑是:
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; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
即Handler
会把Message
的target
设置为自己,并把消息放入到当前线程的MessageQueue
中。
这样Looper.loop()
方法中就可以从MessageQueue
中取出消息,并把消息发送到msg.target(Handler)
去处理,其实msg.target.dispatchMessage(msg)
会调用Handler.handleMessage()
方法。
到这里就完成了整个消息处理模型
的分析。其实 整个Android主线程的UI更新
都是建立在这个模型之上的
用下面这个图总结一下整个运行机制:
Android消息处理模型.png
多线程中的消息处理机制的应用
举一个最典型的例子: 在下载线程中完成了下载,通知主线程更新UI。怎么做呢?
明白了上面Handler/MessageQueue/Looper
的关系后,我们只需要往主线程的MessageQueue
中发送一个更新UI的Message
即可,那怎么往主线程发消息呢?
指定Handler所依附的Looper
对,只需要在构造Hander时把主线程的Looper传递给它即可:
downLoadHandler = Handler(Looper.getMainLooper())
这样downLoadHandler.sendEmptyMessage(2)
就会发送到主线程的MessageQueue
中。handler.handleMessage()
也将会在主线程中回调。
其实更简单的是在主线程中保存一个Handler
成员变量,子线程直接拿这个Handler
发消息即可。
但在多线程使用Handler
时因为涉及到线程切换和异步,要注意内存泄漏和对象可用性检查。比如在更新UI时Activity
是finish
状态,这时候就需要你的update
是否可以继续执行等。
作者:susion哒哒
链接:https://www.jianshu.com/p/8a424c0a46cf