深入了解Handler以及相关问题

目录

  1. 总体介绍
  2. 深入分析
  3. Handler内存泄露问题
  4. 一种优雅的解决方案
  5. 参考

根据Google官方,Handler机制有两个主要作用:1.在未来某个时刻调度消息执行;2.在其他线程中执行相关操作。实际上,我们可以看成是framework层提供的一种线程之间通信方式。通常,我们与UI线程交互时会使用Handler实现,本文主要讨论其源码实现以及相关问题。

总体介绍

  • 事件驱动模型
  • Looper,Message和Hanlder关系图
  • Looper类
    用于建立消息循环并管理消息队列(MessageQueue),不停的从消息队列中抽取消息,分发执行。每个线程只能有一个Looper对象,并且Looper构造方法是私有的,只是创建一个MessageQueue对象并持有。根据Google官方文档,一个Thread实现Looper机制如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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();
    }
    }

prepare()函数定义如下,其中sThreadLocal是静态的TLS对象。启动一个Looper机制的线程尽量使用系统提供的HandlerThread。

1
2
3
4
5
6
7
8
9
10
public static void prepare() {
prepare(true);
}

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));
}

loop()关键代码如下,整体流程就是拿到当前线程的Looper对象,然后从Looper对象的MessageQueue取Message,执行,最后回收消息对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}

  • Message类

Message内部实现了对象缓存池概念,使用Message对象可以从池中取对象,实际上就是一个链表。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

  • Handler类
    最基本的构造方法如下,最重要的就是拿到对应线程的Looper和其中的MessageQueue.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper(); // 当前线程的Looper对象
    if (mLooper == null) {
    throw new RuntimeException(
    "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 当前线程的消息队列
    mCallback = callback; // Handler.Callback 接口
    mAsynchronous = async;
    }

其中,CallBack定义如下:

1
2
3
public interface Callback {
public boolean handleMessage(Message msg);
}

发送消息api有两种:sendXXX和postXXX,其中postXXX都是把Runnable对象封装到Message对象,最终调用sendXXX方法。
Runnable包装成Message的代码如下:

1
2
3
4
5
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

sendXXX方法最终把Message放到队列中,关键是enqueueMessage()函数,最终是调用MessageQueue中方法把该Message按时间顺序插入队列中。

1
2
3
4
5
6
7
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 关键代码:消息的target为Handler对象,最终消息处理是Handler实现的。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

最终处理函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
if (msg.callback != null) { // 1. Message对象的Runnable
handleCallback(msg);
} else {
if (mCallback != null) { // 2. Handler.Callback 对象
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 3. Handler本身HandleMessage()方法,可override
}
}

深入分析

  • MessageQueue类
    这个类比较特殊,翻看源码会发现构造方法中调用了本地方法nativeInit,在本地代码中为消息队列分配内存,实际上可以认为在Native层也搞了一套Handler机制,通过MessageQueue联系在一起。查看对应源码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    NativeMessageQueue::NativeMessageQueue() :
    mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
    mLooper = new Looper(false);
    Looper::setForThread(mLooper);
    }
    }

差不多仿照Java的样子搞了一个Looper,看一下Looper构造函数怎么写的:

1
2
3
4
5
6
7
8
9
10
11
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));

AutoMutex _l(mLock);
rebuildEpollLocked();
}

Android6.0之前使用pipe,6.0改用eventfd,相比于pipe,eventfd缓存大小确定为8Byte,负责线程通信。看一下rebuildEpollLocked函数:

到这里我们可以猜测,底层使用epoll机制实现:有新消息时唤醒Looper线程读取消息队列,否则阻塞。让我们看看源码来验证:

a) 发消息

nativeWake调用了Looper::wake函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif

uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal: %s", strerror(errno));
}
}
}

往mWakeEventFd写东西了!肯定唤醒Looper线程取消息了,让我们继续看。

b) 取消息

主要是MessageQueue的next()函数,这个函数有点复杂,挑重点说一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
...

// 1. 关键代码:
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;
}
...
}
// 2. 关键代码:
// 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);
}
}
}
...
}
}

1.在死循环中调用nativePollOnce方法,直觉告诉我们该方法在没有新消息时会block。在android_os_MessageQueue.cpp中我们发现该函数最终调用了Looper::pollOnce()方法.

其中关键代码是Looper::pollInner,比较复杂,我们只分析相关重要的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    int Looper::pollInner(int timeoutMillis) {
...
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;

// We are about to idle.
mPolling = true;

struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 关键代码: 等待事件到达 或者 超时
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

// No longer idling.
mPolling = false;

// Acquire lock.
mLock.lock();

// Rebuild epoll set if needed.
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}

// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
result = POLL_ERROR;
goto Done;
}

// Check for poll timeout.
if (eventCount == 0) { // 0表示超时
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - timeout", this);
#endif
result = POLL_TIMEOUT;
goto Done;
}

// Handle all events.
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif

for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) { // 如果是 mWakeEventFd 的可读事件,执行 awoke函数,就是从 mWakeEventFd读出数据
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;

// --- 处理Native Message
// Invoke pending message callbacks.
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
...
}

// Release lock.
mLock.unlock();

// --- 处理带有Callback的Response事件
// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) {
...
}
return result; // 消息处理流程是先处理Native Message,再处理Native Request,最后处理Java
// Message。所以有时候上层消息很少,但相应时间却比较长。
}

2.执行IdleHandler消息。当消息队列为空或者未到消息执行时间时,可以执行IdleHandler消息,然后阻塞等待消息到达执行时间或新消息到达。通常可以用于这些场景:要执行但是不知指定多少延迟时间的操作,好像Activity的onStop()函数就是放在这里面执行的。使用demo:

1
2
3
4
5
6
7
8
9
10
11
12
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
/**
* 返回值boolean表示needKeep
* true,表示要保留保留,不移除这个idleHandler,可以反复执行
* false代表执行完毕之后就移除这个idleHandler, 也就是只执行一次
*/
@Override
public boolean queueIdle() {
// todo
return false;
}
});

Handler内存泄露问题

我们通常在Activity中创建Handler时,Lint会提示AndroidLintHandlerLeak,详细介绍如下:

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

这是静态内部类 + 弱引用的方案;还有一种方案是:onDestroy中手动清除掉所有Messages

1
mHandler.removeCallbacksAndMessages(null);

一种优雅的解决方案

好像上述两种方法都不够简洁,从GitHub发现一种优雅的解决方案(https://github.com/badoo/android-weak-handler),实现比较巧妙;其原理就是把Handler封装成WeakHandler,其成员变量有一个静态内部类ExecHandler,WeakHanlder的调用实际上都是调用ExecHandler的方法,关键是所有Callback都是WeakReference,但同时必须做到keep callback in memory,否则GC时会把弱引用的回调给回收了。

  • sendXXXMessage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
private final ExecHandler mExec;

public WeakHandler(@Nullable Handler.Callback callback) {
mCallback = callback; // Hard referencing body
mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
}

private static class ExecHandler extends Handler {
private final WeakReference<Handler.Callback> mCallback;
...
ExecHandler(WeakReference<Handler.Callback> callback) {
mCallback = callback;
}
....
@Override
public void handleMessage(@NonNull Message msg) {
if (mCallback == null) {
return;
}
final Handler.Callback callback = mCallback.get();
if (callback == null) { // Already disposed
return;
}
callback.handleMessage(msg);
}
}

public final boolean sendMessage(Message msg) {
return mExec.sendMessage(msg);
}
...
  • post(Runnable):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 final ChainedRef mRunnables = new ChainedRef(mLock, null);

static class ChainedRef { // 这个双向链表设计比较巧妙
@Nullable
ChainedRef next;
@Nullable
ChainedRef prev;
@NonNull
final Runnable runnable;
@NonNull
final WeakRunnable wrapper;
...
public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
this.runnable = r;
this.lock = lock;
this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
}
...
}

static class WeakRunnable implements Runnable {
private final WeakReference<Runnable> mDelegate;
private final WeakReference<ChainedRef> mReference;

WeakRunnable(WeakReference<Runnable> delegate, WeakReference<ChainedRef> reference) {
mDelegate = delegate;
mReference = reference;
}

@Override
public void run() {
final Runnable delegate = mDelegate.get();
final ChainedRef reference = mReference.get();
if (reference != null) {
reference.remove();
}
if (delegate != null) {
delegate.run();
}
}
}

private WeakRunnable wrapRunnable(@NonNull Runnable r) {
//noinspection ConstantConditions
if (r == null) {
throw new NullPointerException("Runnable can't be null");
}
final ChainedRef hardRef = new ChainedRef(mLock, r);
mRunnables.insertAfter(hardRef);
return hardRef.wrapper;
}

public final boolean post(@NonNull Runnable r) {
return mExec.post(wrapRunnable(r));
}
...

参考

[1] http://developer.android.com/intl/zh-cn/reference/android/os/Handler.html
[2] http://www.69900.com.cn/a34140974/article/details/50638089
[3] http://gityuan.com/2015/12/27/handler-message-native/
[4] https://github.com/badoo/android-weak-handler