什么是 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 {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e("tag", "thread name:" + Thread.currentThread().getName());
//更新UI
break;
}
}
}
MyHandler myHandler = new MyHandler();
创建线程,处理耗时逻辑,然后发送消息
new Thread("t1") {
@Override
public 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") {
@Override
public void run() {
Looper.prepare();//创建Looper
Handler 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 方法获取 Message
if (msg == null) {
// No message indicates that the message queue is quitting.
// msg == null 表示 MessageQueue 正在退出(调用了quit等方法)
return;
}
//剔除代码
try {
// msg.target 就是发送消息的Handler,因此这里将消息交回 Handler
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//剔除代码
}
}
前面说了,Looper 通过无限循环的方式获取 消息队列 中的消息
for (;;)
死循环从消息队列中获取 消息 ,当 msg == null
跳出循环。
通过调用 Looper 的 quit()
或者 quitSafely()
让 Looper 退出循环
// quit
public 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 iteration
int 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 {
@Override
public void handleMessage(Message msg) {
//处理消息
}
}
而使用 Callback
接口时
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
当我们不想派生子类时,就可以通过 Callback
的方式实现
OK,再回过头来看看 Handler 运行在主线程 这个问题。
其实 Handler 运行在哪个线程,是由创建 Handler 的 Looper 决定的,简单看个例子(onCreate()方法中)
new Thread("t1") {
@Override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
}.start();
SystemClock.sleep(100);//确保成功获取到 looper
Handler handler = new Handler(looper, new Handler.Callback() {
@Override
public 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 就是运行在主线程的?看下面的例子
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Message mess = Message.obtain();
mess.what = 1;
Handler handler = new Handler(new Handler.Callback() {
@Override
public 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 属于哪个线程决定
讲完...
网友评论