运行机制"/>
Android Handler 运行机制
什么是 Handler?有什么用?
在 android 应用开发中经常用到 Handler ,大概知道 Handler 常被用来处理线程间通信,比如:在子线程中处理某个逻辑后通过 Handler 发送消息的方式,切换回 UI 线程更新界面
Handler 到底是什么呢?总能听到最傻瓜式的问法,那么就来简单回答一下:Handler 不是一个具体的对象,暂且说是一套解决方案吧,在 Android 中处理消息 (Message)的一套方案。
我们通过研究 Handler 的运行机制来了解 android 的消息机制
什么是 Handler 运行机制?
这里需要引入几个概念和名词 Message
/ MessageQueue
/ Looper
以下请允许我用不太恰当地方式简单解释一下这几个概念
Message :
消息
MessageQueue :
消息队列
Looper :
无限循环地从队列中获取消息交给 Handler
Handler :
发送消息,处理消息
如果有人问你, Handler 运行机制 是怎么样的,你就告诉他:
Handler 发送消息(Message) 到消息队列中(MessageQueue) ,Looper 通过无限循环的方式不断向消息队列中获取新添加的消息 然后交给 Handler ,最终消息回到 Handler 中被处理
(平常开发中)由于 Handler 创建在主线程,处理消息的方法也运行在主线程,因此上述中消息被 Looper 交回给 Handler 的时候就实现了“线程切换”
喔~这个6,这个6,这个思想非常棒,仔细想想在子线程中做了一堆操作,什么添加消息到队列,我们不管,只认准最后 Looper 把消息交回给了 Handler ,又因为 Handler 运行在主线程中,因此最后处理消息时就在主线程了(根本原因是创建Handler 的 Looper 默认使用主线程的 Looper )
理论往往是抽象枯燥的,我们总希望通过熟悉的代码来慢慢体会,来看下日常开发中常用Handler使用场景,在子线程中处理耗时逻辑,然后再发送消息到主线程中更新UI
在主线程中创建Handler处理类
class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.e("tag", "thread name:" + Thread.currentThread().getName());//更新UIbreak;}}}MyHandler myHandler = new MyHandler();
创建线程,处理耗时逻辑,然后发送消息
new Thread("t1") {@Overridepublic void run() {//耗时操作try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Message message = Message.obtain();message.what = 1;//发送消息myHandler.sendMessage(message);}}.start();
这里可以看到,我们在主线程(activity运行在主线程中)创建Handler,然后创建一个子线程 t1 ,在 t1 中处理耗时逻辑,再通过 sendMessage() 发送消息,就是这么一个简单的代码,能够实现”线程切换”的效果,到底是怎么实现的呢?
开发中我们能接触到的只有 Handler ,对于 MessageQueue 和 Looper 这两个概念比较陌生,就上述的例子中也完全没有体现这些字眼,但是对于 Handler 的运行他们起到了不可缺少的重要作用。
Handler 通过 sendMessage 发送消息后,将 Message 发送到了哪里呢? 又是怎么拿到这些 Message 的呢?
MessageQueue 消息队列工作原理
MessageQueue 翻译为消息队列。简单的说就是装载消息(Message) 的容器。容器?那不是很简单,只要负责 添加 和 读取(移除) 内容就好了。
当 Handler 通过 sendMessage 方法发送 Message 时, MessageQueue 就会调用 enqueueMessage 方法把这条消息添加到自身容器中。然后呢?然后就放着呗,等着别人来取,当有人来取 Message 的时候,通过 next 方法取出一条消息并将消息从自身容器中移除,这就是消息队列的工作职责
这里注意,虽然叫做消息队列,但是内部实现并不是用队列,而是单链表的数据结构来维护消息列表
MessageQueue : 添加消息到队列 enqueueMessage
上面说 sendMessage 时 MessageQueue 就会调用自身的 enqueueMessage
将消息添加到自身容器,怎么实现的呢?Handler 和 MessageQueue 两者又是怎么关联起来的?这时候我们就需要通过阅读一下源码啦
等一下,别走啊,这源码很简单的,不用一头扎入,看个大概思路就行
我们知道 Handler 发送消息的方法有很多,先不管,需要知道的是最后调用的都是同一个 sendMessageAtTime()
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);}
这个就是 Handler 的 sendMessageAtTime
方法的源码啦,很简单,将一个 mQueue
(特别注意这个 mQueue)赋值给 MessageQueue
。 MessageQueue
如果为空,抛异常,不为空当做一个参数传递给 enqueueMessage
(Handler 的方法),恩,这里问题不大,很普通的写法,接着看看 Handler 的 enqueueMessage
方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
丢掉前面无关的代码,直接聚焦 queue.enqueueMessage(msg, uptimeMillis)
这行代表表示,最终调用了 MessageQueue
的 enqueueMessage
方法将消息添加进队列
到这里已经很清晰可以看到 MessageQueue 将消息加入队列的逻辑了,不用看都知道, enqueueMessage
具体实现肯定是操作链表添加内容
怎么样?是不是很简单,很清晰的看到
Handler 调用 sendMessage 方法时 MessageQueue 会通过 enqueueMessage 方法将 message 添加到自身容器中
嗯,细心的同学或许就会问了,这个 MessageQueue
是哪里来的,你突然引入一个 MessageQueue
然后就开始添加 消息了,也没说他是哪里崩出来的呀
回到刚才那个 sendMessageAtTime()
方法,第一行代码出现:MessageQueue queue = mQueue;
原来 MessageQueue
是通过 mQueue
赋值的。
在 Handler 类中搜索 mQueue
,找啊找啊,最后在 Handler 构造方法中找到它
public Handler(Looper looper, Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}
没错就是它 mQueue
,它是通过 looper.mQueue
得到的
而且我们通过其他的构造函数发现 Handler 的所有构造函数最终都是调用这个方法,也就是说,我们在创建 Handler 的时候就创建了这个 MessageQueue
soga 原来 MessageQueue
是通过 Looper
得到的,那 Looper
又是什么?乘胜追击看看这个 Looper
是什么。
Looper 工作原理
Looper
在 android 消息机制中扮演着 循环从队列中获取消息的角色,就是说它会无限循环的查询 消息队列中是否有新的消息,如果有就获取到这个消息,然后交给Handler,如果没有就阻塞在那里
先看看 Looper
的构造方法
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
在 Looper 构造方法中,创建了一个 MessageQueue 即消息队列,然后获取当前线程保存起来。
我们知道 Handler 工作需要 Looper 的支持,如果没有 Looper 的线程中使用 Handler 就会报错,那如何为一个线程创建一个 Looper 呢? 很简单, 通过 Looper.prepure()
就可以为一个线程创建 Looper ,接着调用 Looper.loop()
来开启消息循环。示例如下
new Thread("t1") {@Overridepublic void run() {Looper.prepare();//创建LooperHandler handler = new Handler();//Handler 使用Looper.loop();//开启消息循环}}.start();
Looper.prepare()
创建 Looper 很简单不说了,下面有个 Looper.loop()
开启消息循环,这个是 Looper 的重心啦,看看怎么实现的
loop()
的源码比较多,不必每一行都去分析,我们通过剔除一些代码,让源码看起来简洁一些,这样就不会掉进源码的漩涡中不能自拔。
public static void loop() {//剔除代码final MessageQueue queue = me.mQueue;//...for (;;) {Message msg = queue.next(); // 调用 MessageQueue 的 next 方法获取 Messageif (msg == null) {// No message indicates that the message queue is quitting.// msg == null 表示 MessageQueue 正在退出(调用了quit等方法)return;}//剔除代码try {// msg.target 就是发送消息的Handler,因此这里将消息交回 Handlermsg.target.dispatchMessage(msg);end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}//剔除代码}}
前面说了,Looper 通过无限循环的方式获取 消息队列 中的消息
for (;;)
死循环从消息队列中获取 消息 ,当 msg == null
跳出循环。
通过调用 Looper 的 quit()
或者 quitSafely()
让 Looper 退出循环
// quitpublic void quit() {mQueue.quit(false);}// quitSafely()public void quitSafely() {mQueue.quit(true);}
我们看到 Looper 的 quit()
或者 quitSafely()
方法调用的是 MessageQueue
的 quit()
方法,使得 msg 返回 null 从而让 Looper 退出循环。这两个方法的区别是: quit 是直接退出 Looper; quitSafely 会设置一个退出标识,等到队列中的消息都处理完了,再安全退出。Looper 退出后 Handler 就无法发送消息了
loop() 中 获取到消息通过 msg.target.dispatchMessage(msg)
将消息 message 交给 Handler 的 dispatchMessage
方法处理,这也就是为什么 能够实现线程切换的关键所在,因为 Handler 运行在主线程
MessageQueue : 获取消息 next
在 Looper.loop()
中我们看到通过 MessageQueue
的 next
方法获取队列中的消息
Message next() {// 剔除代码int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}// 阻塞nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message. Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// Stalled by a barrier. Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {//存在消息,未到执行时间// Next message is not ready. Set a timeout to wake up when it is ready.nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {//存在消息,获取消息返回 // Got a message.mBlocked = false;//表示目前没有阻塞if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;//将 msg 从 MessageQueue 链表中移除if (DEBUG) Log.v(TAG, "Returning message: " + msg);msg.markInUse();return msg;}} else {// 没有消息处理 进入阻塞// No more messages.nextPollTimeoutMillis = -1;}// MessageQueue 退出 返回 null// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}//剔除代码}//剔除代码}}
next
通过 for (;;)
死循环不断获取新增的消息,返回给 Looper,如果没有消息,就一直阻塞在这里,当有新消息到达,next
会返回这条消息,并且从链表中删除
Handler 工作原理
Handler 的工作原理就比较简单啦,主要负责 发送消息 和 处理消息
发送消息 主要通过一些列 post
和 send
方法来实现,具体可以查查接口文档,通过源码可以看到最终都是调用 sendMessageAtTime()
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);}
sendMessageAtTime()
的源码很简单,调用 MessageQueue
的方法 enqueueMessage
将 Message 添加到消息队列,同时 msg.target = this;
将 Handler
自身赋值给了 msg.target
可以发现, Handler 发送的消息过程是向消息队列添加一条消息,这时 MessageQueue 的 next 方法就会将这条消息返回给 Looper,Looper 收到消息后开始处理,最终通过 msg.target.dispatchMessage(msg);
将消息交给 Handler ,即 Handler 的 dispatchMessage
方法会被调用,这时 Handler 就进入了消息处理阶段
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}
Handler 处理消息的过程:
首先检查 Message
的 callback
是否为 null。 不为 null 直接通过 handleCallback
来处理消息。Messsage
的 callback
是一个 Runnable
对象,也就是 Handler 的 post 系列方法所传递的 Runnable 参数。 handler.post(runnable);
handleCallback
private static void handleCallback(Message message) {message.callback.run();}
然后检查 mCallback
是否 null。不为 null 直接通过 mCallback
的 handleMessage
处理消息。mCallback
是一个接口,定义如下
/*** Callback interface you can use when instantiating a Handler to avoid* having to implement your own subclass of Handler.* //不需要通过一个 Handler 的子类,就可以创建一个 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 实现一个 Handler 的子类来实现
class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {//处理消息}}
而使用 Callback
接口时
Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {return false;}});
当我们不想派生子类时,就可以通过 Callback
的方式实现
OK,再回过头来看看 Handler 运行在主线程 这个问题。
其实 Handler 运行在哪个线程,是由创建 Handler 的 Looper 决定的,简单看个例子(onCreate()方法中)
new Thread("t1") {@Overridepublic void run() {Looper.prepare();looper = Looper.myLooper();Looper.loop();}}.start();SystemClock.sleep(100);//确保成功获取到 looperHandler handler = new Handler(looper, new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());return false;}});Message mess = Message.obtain();mess.what = 1;handler.sendMessage(mess);
解释一下代码:创建一个子线程 t1 ,为这个线程创建 Looper (Looper.prepare();
),然后获取 t1 线程的 Looper 用于创建 Handler
在 Handler 的 handleMessage 中处理消息,打印 Handler 所在的线程,结果为: t1
那为什么默认创建的 Handler 就是运行在主线程的?看下面的例子
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Message mess = Message.obtain();mess.what = 1;Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());return false;}});handler.sendMessage(mess);}
在 Handler 的 handleMessage 中处理消息,打印 Handler 所在的线程,此时,结果为: main
看一下 Handler 构造方法的代码(在不指定Looper的时候)
public Handler(Callback callback, boolean async) {//剔除代码mLooper = Looper.myLooper();//获取当前线程Looper//剔除代码}
通过代码可以看出,在创建 Handler 不指定 Looper 时,Handler 的构造方法会获取当前线程的 Looper ,由于activity 的 onCreate()
方法运行在主线程,所以 Looper 也属于主线程,通过此 Looper 创建的 Handler 当然也运行在主线程啦~
由此可以验证 Handler 运行在哪个线程由 创建 Handler 的 Looper 属于哪个线程决定
讲完…
更多推荐
Android Handler 运行机制
发布评论