一 Handler简介
1、什么是Handler
Handler是Android中的消息机制,一般用来进行线程间通讯。Android只所以少有线程问题,很大一部的原因就是Handler。Handler采用生产者设计-消费者设计模式,实际上是内存共享的方案。通过优秀的内存管理设计方案,保证了Android线程间不会互相干扰,防止内存抖动。整个工作流程如下图:
2、Handler的使用场景
二 Handler的源码解析
Android的Handler整套机制主要包含了Handler、Looper、MessageQueue、Message、ThreadLocal这几个类互相配合共同构成了Handler消息机制。
下面先对Handler处理消息的流程进行整体的分析。
1、消息的发送
我们在平常开发过程中,一般都是在子线程中调用Handler的sendMessage、post等方法去发送消息的。通过源码我们知道最终会调用MessageQueue的enqueueMessage方法。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里总结了Handler的主要方法如下图
那么我继续向下看MessageQueue的enqueueMessage 方法是如何处理消息的:
boolean enqueueMessage(Message msg, long when) {
...
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
我们看到MessageQueue的enqueueMessage方法首先判断有没有当前消息和msg.when也就是消息的延迟时间是否为0或者小于当前消息的时间。如果条件成立那么就将新消息插入到队列的头部。否则循环比较消息的延迟时间,然后按顺序插入到合适的位置。以上就是消息的发送。
2、消息的处理
Handler机制是通过Looper来处理消息的。要使用Handler那么线程中必需有且只有一个Looper,并且要调用loop方法。Looper的loop方法就是处理消息的。还是老套路先上源码:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
......
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);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
.....
msg.recycleUnchecked();
}
}
我们看到loop函数维持了一个死循环,在这个循环中调用MessageQueue的next方法从消息队列中取消息, 然后调用msg.target.dispatchMessage(msg)进行消息回调。
那么这里next方法是如何取消息的呢?
@UnsupportedAppUsage
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
.......
}
}
我们看到next方法很简就是循环遍历消息,然后判断消息是否到达执行时间,如果没到就调用nativePollOnce方法进行线程阻塞,如果到达执行时间就将该消息返回到Looper的loop方法里。然后调用handler的dispatchMessage方法进行回调。
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里有没有很熟悉啊?handleMessage,没错就是我们主线程更新UI的地方。以上就是Handler消息的主流程。
总结:
- 以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队MessageQueue中。
- Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。
- 在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。
三 Handler的 注意点
1、Handler实质是内存共享,那么它是如何内存共享的?
由开头Handler的原理图我们知道线程是Handler消息机制的动力,通过Looper.loop方法循环处理消息。每一个Message就是一个内存单元。通过Handler 将Message发送到MessageQueue队列中。Looper.loop 循环取出Message再回调到Handler的handlerMessage中。
2、一个线程有几个Handler?
一个线程可以有任意个Handler。每个Handler都会记录在其发送Message的target就是该Handler。这样Looper处理消息后就会调用对应handler的回调方法。
3、一个线程有几个Looper?如何保证?
一个线程只能有一个Looper,是通过ThreadLocal保证的。Looper有一个静态常量sThreadLocal,并且Looper的构造方法是私有的,只能通过静态方法prepare和prepareMainLooper创建。调用prepare方法会首先调用readLocal.get获取当前线程的Looper,如果不为null就会跑出异常,否则就创建Looper实例并保持到ThreadLocal。
那么ThreaLocal是怎么保证的呢?原来每个线程中只有有一个ThreadLocal.ThreadLocalMap,ThreadLocalMap内部有一个Entry数组。ThreadLocal.set就是以ThreadLocal作为key将Looper 存到ThreadLocalMap中的。这样每个ThreadLocal只能有一个值Looper,而Looper只有一个ThreadLocal所以一个线程只有一个Looper。
由此我们可知,每个线程只能有一个Looper和一个MessageQueue,可以有多个Handler。
4、为何主线程可以new Handler? 如果在子线程中new Handler需要做些什么?
public Handler(@Nullable Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我看到创建Handler时会调用Looper.myLooper()获取Looper(这里内部其实是从ThreadLocal中获取当前线程的Looper)判断如果looper为null就会抛出异常。所以要想创建Handler,当前线程必需有Looper实例。
主线程之所以可以直接new Handler就是因为在ActivityThread的main函数中初始化了Looper。源码如下:
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
子线程要想new Handler那么我们就要自己手动调用Looper.prepare为线程创建Looper实例。然后还需要调用Looper.loop()使其运转起来,否则Message是不会被处理的。在子线程任务结束时还要调用Looper的quitSafely(),quitSafely()会调用MessageQueue的quit()方法,清空所有Message,并调用nativeWake()方法唤醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循环继续执行,接下来发现Message为null后就会结束循环,Looper结束。如此便可以释放内存和线程。如果不调用的话会就会一直阻塞线程,使线程不能被回收。
5、Handler的内存泄漏原因。
回答这个问题前我们要知道什么是内存泄漏,内存泄漏是指本该被GC的对象因为某种原因而没有被回收,造成内存得不到释放。在Java虚拟机中GC是通过GCRoot判断回收的。
那么Handler中产生内存泄漏的话,谁才是GcRoot呢?
这里很多人常常会搞错,认为是ThreadLocal.ThreadLocalMap导致的内存泄漏。实际上这是错误的。
非静态内部类持有外部类对象的引用。Handler持有Activity,Message又持有Handler的引用。Message最终会被添加到MessageQueue中,MessageQueue是Looper的成员,所以Looper持有MessageQueue。在Android主线程中回调用Looper.prepareMainLooper方法,在该方法中会把lopper对象赋值给sMainLooper这个static成员。所以Handler中真正导致内存泄漏的GCRoot是sMainLooper。
总结一下引用关系:ThreadLocal.ThreadLocalMap——>Entry(WeakReference)——>static Looper sMainLooper(GcRoot)——>MessageQueue——>Message——>Handler——>Activity
那么如何处理Handler的内存泄漏呢?
可以定义静态内部类继承Handler,静态内部类中要使用弱引用Activity。然后在Activity的onDestory方法执行时,调用Handler的removeCallbacksAndMessages移除所有消息和回调。
6、多个Handler向MessageQueue添加消息,内部是如何保证线程安全的。
Messageueue的enqueueMessage函数中会通synchronized对象锁,来处理消息的插入。同样的next方法取出消息时也有synchronized对象锁,并且这两个函数使用的同一个对象锁。所以无论是添加消息还是读取消息都是互斥的,能够保证线程安全。
值得注意的是,由于锁的存在,所以不能保证延迟消息的时间一定准确噢。
7、我们使用Message时应该如何创建。
Handler消息机制实际是内存共享,这里其实就是共享的Message,每个message都是一个内存单元。Handler有大量消息,如果每次都new Message,那么就会有大量对象创建和回收,造成内存抖动。Handler使用享元设计模式完美的解决了这个问题。
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Message回收时会调用其recycleUnchecked方法,将数据致空,然后放到 sPool池子里缓存等待复用。我们可以调用obtain方法从池子里拿出Message进行复用。
8、Handler的消息屏障和异步消息。
Handler的消息屏障和异步消息时成对出现的,是为了处理优先级高的消息而设计的。在Android框架中View的刷新使用了该方案,解决了View刷新优先级的问题。
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这里使用MessageQueue的postSyncBarrier方法发送屏障消息。所谓屏障消息就是Message的target为null的消息,它是为异步消息做标记的。发出一个屏障消息紧接发出一个异步消息,异步消息在屏障消息后面。而Looper的loop方法会从队列头部不断取出消息进行处理,当遇到屏障消息时,就会查找异步消息,并处理它。当异步消息处理完成后会调用MessageQueue的removeSyncBarrier移除屏障消息。
Message next() {
......
for (;;) {
.....
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());
}
.....
}
}
9、IdleHandler是什么?怎么使用,能解决什么问题?
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
@UnsupportedAppUsage
Message next() {
.....
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
.....
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
.....
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 {
.....
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}