Handler是一套消息处理机制,可以通过它发送消息,并将所有的消息在指定的线程统一处理。
Handler是什么?
Handler是一套消息处理机制,可以通过它发送消息,并将所有的消息在指定的线程统一处理。
为什么用Handler?
Android是单线程UI模型,在其他线程更新UI会报CalledFromWrongThreadException异常,所以当我们在子线程跟新UI时,需要先创建一个主线程的Handler,向此handler发送跟新UI的消息,然后就可以在主线程处理更新UI操作。
Handler怎么使用?
1.子线程向UI线程发消息
public static void main(String[] args) {
UIHandler uiHandler = new UIHandler();
OtherThread otherThread = new OtherThread(uiHandler);
otherThread.start();
otherThread.sendMessage();
}
static class UIHandler extends Handler {
public UIHandler() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
static class OtherThread extends Thread {
private final Handler handler;
public OtherThread(Handler handler) {
this.handler = handler;
}
public void sendMessage() {
Message message = Message.obtain();
handler.sendMessage(message);
}
}
2.UI线程向子线程发消息
public static void main(String[] args) {
OtherThread otherThread = new OtherThread();
otherThread.start();
otherThread.sendMessage();
}
static class OtherThread extends Thread implements Handler.Callback{
private Handler handler;
@Override
public void run() {
super.run();
Looper.prepare();
//创建Handler之前一定要先调用Looper.prepare()创建Looper,否则会报异常
//开发中可以直接使用HandlerThread来实现,HandlerThread里面和这里一样对Looper做了封装
handler = new Handler(this);
Looper.loop();
}
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
public void sendMessage(){
Message message = Message.obtain();
handler.sendMessage(message);
}
}
Handler源码分析

上图是Handler消息机制的简要流程示意图,主要包含三个类:
Handler: 发送消息到MessageQueue,以及处理消息。Looper: 内部持有一个MessageQueue,通过loop()方法不断从MessageQueue中取出Message交由对应的信息处理器(Handler)处理,线程切换也是在这里完成。MessageQueue: 消息队列,其实是维护了一个根据消息【执行时间先后】连接起来的单向链表结构,当没有消息时会阻塞。
Handler 是标准的事件驱动模型,简单说就是Handler发送消息到MessageQueue,Looper从MessageQueue取出消息下发到对应的Handler处理。
下面直接通过代码分析,看一下具体实现细节。
在通过Handler发送消息前需要先调用Looper.prepare()和Looper.loop()方法来创建并启动Looper,不然会报异常。
主线程的Looper不需要我们创建是因为在Android程序的入口已经调用了Looper.prepareMainLooper()及loop()方法。
//ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
下面先看一下Looper.prepare()方法:
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
该方法创建一个Looper保存到ThreadLocal里面,Looper和Thread是一对一绑定关系。在创建Looper时会初始化一个MessageQueue消息队列,MessageQueue和Looper也是一对一绑定关系。该方法一个线程只能调用一次,否则报上面的异常。
然后看一下Looper.loop()方法:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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;
}
...
msg.target.dispatchMessage(msg);
...
//循环复用
msg.recycleUnchecked();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
先从ThreadLocal中获取当前线程的Looper,拿到MessageQueue,然后开启一个无限循环,不断调用MessageQueue的next()方法获取下一个消息,然后分发到Message中的Handler中,最后把Message回收服用。
下面看一下MessageQueue的next()方法
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
...
for (;;) {
...
//nextPollTimeoutMillis>0时阻塞线程,nextPollTimeoutMillis==-1时一直阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
// 尝试取出下一个消息返回
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//处理同步障碍,同步障碍就是msg.target==null的消息
//当消息队列有同步障碍,同步消息下发受到阻碍,异步消息不受影响
//阻塞消息通过postSyncBarrier()发送,直到通过调用removeSyncBarrier()来释放指定的障碍
if (msg != null && msg.target == null) {
//将同步的消息跳过,直到下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 消息还没到触发时间,设置要休眠的时间
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
//处理IdleHandler,IdleHandler是Handler提供的一种在消息队列空闲时执行任务的机制,即空闲任务
// IdleHandler只有当MessageQueue处于空闲(MessageQueue为空或者第一个要发送的消息没到触发时间)时才会执行
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// mIdleHandlers里没有要执行的IdleHandler,跳过下面的处理IdleHandler逻辑,继续下次循环
if (pendingIdleHandlerCount <= 0) {
//下次消息需要阻塞线程,所以标识当有新消息时需要被唤醒
mBlocked = true;
continue;
}
//把mIdleHandlers保存到mPendingIdleHandlers,后面for循环会删除mIdleHandlers中的IdleHandler
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 执行空闲任务
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// keep返回false,执行一次即从mIdleHandlers中移除
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//空闲任务每次获取消息只执行一次,所以将pendingIdleHandlerCount值为0,下一次for循环就不会再执行IdleHandler的逻辑.
pendingIdleHandlerCount = 0;
// 当IdleHandler执行完,之前没到触发时间的消息可能已经可以执行,
// 所以等待时间置为0,直接看一下有没有消息需要发送
nextPollTimeoutMillis = 0;
}
}
这个方法用来获取下一个Message。首先开启一个无限循环,然后进入同步锁获取下一个消息,如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;如果消息此刻到时间了,返回msg。
Looper准备好了,下面我们用Handler发送消息:
调用Handler的sendMessage()方法,最终会调用enqueueMessage()方法。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
msg.target = this;
...
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法先将handler保存到Message中,然后调用MessageQueue的enqueueMessage()方法。
下面看一下MessageQueue的enqueueMessage()方法:
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//插入队列的头部
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 插入队列中间,通常我们不必唤醒事件队列,除非队列头部有障碍物,并且消息是队列中最早的异步消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//插入位置的前一个消息
Message prev;
for (;;) {
prev = p;
p = p.next;
//时间小于下个消息时间时跳出循环
if (p == null || when < p.when) {
break;
}
//异步消息不在头部,不需要唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将消息加入单向链表
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这个方法主要是将消息根据时间排序将消息插入到MessageQueue中,根据是否需要唤醒调用nativeWake()方法。如果调用唤醒方法MessageQueue的next()方法将不再阻塞,Looper将继续循环从MessageQueue中获取消息交由对应的Handler处理。到这Handler消息机制Java部分就完成了。
线程之间切换
分析了这么多,那Handler是怎么实现线程切换的呢?从头到尾也没看到切换线程的代码啊😂。
通过上面的分析我们知道,当我们在A线程调用Handler发送消息,在B线程调用Looper.loop(),不断从MessageQueue中取出消息分发到Handler,所以消息处理是在B线程,这就完成了消息线程的切换。
pipe/epoll机制
类比Java中的PipedInputStream和PipedOutputStream,用于线程间通信,当缓冲区没有数据时,输入流所在的线程将处于阻塞状态,输出流向缓冲区写入一个数据时,输入流所在的线程将解除阻塞。
Looper死循环为什么不会导致应用卡死?
Looper通过MesssageQueue的next()方法获取到下一个消息,当取不到消息时,会调用nativePollOnce(ptr,-1)释放CPU资源进入休眠状态;直到下次发送新消息激活nativeWake()方法,才会唤醒for循环继续获取消息。native层是使用了Linux内核的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。而造成ANR是因为在单次循环中做了耗时操作,其他消息得不到及时处理导致的。
MessageQueue如何管理消息?
MessageQueue中的消息都是按照消息触发时间顺序由近及远进行的排序。
插入消息时,首先找到比发送消息触发时间小的最后一个消息,将靠前消息的next赋值成当前的消息,当前消息的next赋值靠前消息的next。
取出消息时,从队列取第一个消息作为待发送的消息,如果该消息是障碍消息,则取队列中第一个异步消息作为待发送的消息。如果取到的消息触发时间大于当前时间,则不返回该消息,进入下次循环先阻塞到消息触发时间后再返回,如果大于等于当前时间则直接返回,如果没有取到消息则一直阻塞队列。
IdleHandler 有什么用?
IdleHandler 是 Handler 提供的一种在消息队列空闲时,执行任务的机制,即空闲任务。当 MessageQueue 当前没有立即需要处理的消息时,会执行 IdleHandler。
MessageQueue 获取一次消息需要进行几次 for 循环?
最多三次,当取到的消息时间小于等于当前时间,只需要一次 for 循环。当取到的消息时间大于当前时间,如果没有 IdleHandler,需要两次 for 循环;如果有 IdleHandler,需要三次 for 循环。
当 mIdleHanders 一直不为空时,为什么不会进入死循环?
只有在 pendingIdleHandlerCount 为 -1 时,才会尝试执行 mIdleHander ,pendingIdlehanderCount 在 next() 中初始时为 -1,执行一遍后被置为 0,所以不会重复执行。
Handler内存泄漏
class HandlerActivity extends AppCompatActivity {
private final Handler mHandler = MyHandler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.sendEmptyMessageDelayed(1, 1000);
}
class MyHandler extends Handler {}
}
上面Handler的实现方式,当我们HandlerActivity在1000毫秒内退出就会产生内存泄漏。这是因为Handler被Message持有,保存在其target当中,而Message又被保存在MessageQueue中,所以Handler不会被GC,而Handler是一个内部类,持有HandlerActivity的引用,所以HandlerActivity也不会被回收,所以导致了内存泄漏。
主线程的Looper什么时候退出循环?
APP退出的时候。
//ActivityThread.java H
public void handleMessage(Message msg) {
switch (msg.what) {
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
}
}
832

被折叠的 条评论
为什么被折叠?



