• 前言
    • 喜欢就争取 得到就珍惜 错过就忘记

参考博客

  本文中的所有内容大部分来源于网络资料,如有侵权请联系本人修改或删除,请大家多多支持原创!非常感谢!

Handler

Handler机制简介

  Android的UI框架基于事件驱动,所有与界面相关的操作必须在主线程(也称为UI线程)中执行。这是由于Android的UI框架不是线程安全的,即多个线程同时操作UI可能引发各种问题,例如UI元素的状态不一致、布局错乱、甚至崩溃等。故多线程可并发操作UI组件,则出现了Handler。Handler是一套Android消息传递机制/异步通信机制。在多线程的应用场景中,将工作线程中需要更新UI的操作信息传递到UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。

相关概念

  关于Handler机制有5个关键对象,即Handler、Message、MessageQueue、Looper

重识Handler

  每个Handler都会跟一个线程绑定,并与该线程的MessageQueue关联一起,使用Handler发送并处理一个与线程关联的Message和Runnable,从而实现消息的管理i以及线程间通信。(注意:Runnable会封装进一个Message,所以本质上还是一个Message)

Handler的基本用法

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

//在主线程中创建Handler实例
android.os.Handler handler = new Handler(){
//通过复写handleMeeagge()从而确定更新UI的操作
@Override
public void handleMessage(final Message msg)
{
//这里接受并处理消息
}
};

//创建所需要的消息对象
Message msg = Message.obtain();//实例化消息对象
msg.what = 1;//消息标识
msg.what = "AA";//消息内容存放
//在工作线程中,通过Handler发送消息到消息队列中
//可以通过sendMessage()/post()
//多线程可采用AsyncTask、继承Thread类、实现Runnable
handler.sendMessage(msg);
handler.post(new Runnable(){
@Override
public void run(){
//需要执行的UI操作
}
});

Handler原理解析

Handler与Looper的关联

  实际上我们在实例化Handler的时候Hnadler会去检查当前线程的Looper是否存在,如果不存在则会报异常,也就是说在创建Handler之前一定需要先创建Looper。

  • frameworks/base/core/java/android/os/Handler.java
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

//Line 197
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

//检查当前的线程是否有Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//Looper持有一个MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
mIsShared = false;
}

  Looper提供了Looper.prepare()方法来创建Looper,并且会借助ThreadLocal来实现与当前线程的绑定功能。Looper.loop()则会开始不断尝试从MessageQueue中获取Message,并分发对应的Handler。也就是说Handler跟线程的关联是靠Looper来实现的。

  • frameworks/base/core/java/android/os/Looper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

//Line 101
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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));
}

Message的存储与管理

  Handler提供了一系列的方法让我们来发送消息,如send()/post(),不管我们调用什么方法,最终都会走到MessageQueue.enqueueMessage(Message, long)方法。以post(Runnable)方法为例:

  • frameworks/base/core/java/android/os/Handler.java
    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
    //Line 423

    /**
    * Causes the Runnable r to be added to the message queue.
    * The runnable will be run on the thread to which this handler is
    * attached.
    *
    * @param r The Runnable that will be executed.
    *
    * @return Returns true if the Runnable was successfully placed in to the
    * message queue. Returns false on failure, usually because the
    * looper processing the message queue is exiting.
    */
    public final boolean post(@NonNull Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
    }

    //Line 689

    /**
    * Enqueue a message into the message queue after all pending messages
    * before (current time + delayMillis). You will receive it in
    * {@link #handleMessage}, in the thread attached to this handler.
    *
    * @return Returns true if the message was successfully placed in to the
    * message queue. Returns false on failure, usually because the
    * looper processing the message queue is exiting. Note that a
    * result of true does not mean the message will be processed -- if
    * the looper is quit before the delivery time of the message
    * occurs then the message will be dropped.
    */
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
    delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    /**
    * Enqueue a message into the message queue after all pending messages
    * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
    * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
    * Time spent in deep sleep will add an additional delay to execution.
    * You will receive it in {@link #handleMessage}, in the thread attached
    * to this handler.
    *
    * @param uptimeMillis The absolute time at which the message should be
    * delivered, using the
    * {@link android.os.SystemClock#uptimeMillis} time-base.
    *
    * @return Returns true if the message was successfully placed in to the
    * message queue. Returns false on failure, usually because the
    * looper processing the message queue is exiting. Note that a
    * result of true does not mean the message will be processed -- if
    * the looper is quit before the delivery time of the message
    * occurs then the message will be dropped.
    */
    public boolean sendMessageAtTime(@NonNull 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);
    }

    //Line 778
    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);
    }

  MessageQueue顾名思义,就是个队列,负责消息的入队出队

Message的分发与处理

  Looper.loop()负责对消息的分发

  • frameworks/base/core/java/android/os/Looper.java
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//Line 258

/**
* Poll and deliver single message, return true if the outer loop should continue.
*/
@SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
"ClearIdentityCallNotFollowedByTryFinally"})
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}

// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;

final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;

final boolean hasOverride = thresholdOverride >= 0;
if (hasOverride) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0 || hasOverride)
&& (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0 || hasOverride);

final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;

if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}

final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
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);
}
}
if (logSlowDelivery) {
if (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
me.mSlowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}

if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}

// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//回收meesage
msg.recycleUnchecked();

return true;
}


/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
"ClearIdentityCallNotFollowedByTryFinally",
"ResultOfClearIdentityCallNotStoredInVariable"})
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}

me.mInLoop = true;

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", -1);

me.mSlowDeliveryDetected = false;

for (;;) {
//不断从MessageQueue获取消息
if (!loopOnce(me, ident, thresholdOverride)) {
//退出Looper
return;
}
}
}

  loop()里调用了MessageQueue.next():

  • frameworks/base/core/java/android/os/MessageQueue.java
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
96
97
98
99
100
101
102
103
104
105
106
107
108

@UnsupportedAppUsage
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

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

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
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;
}
}

  还调用了msg.target.dispatchMessage(msg),msg.tartget就是发送该消息的Handler,这样就回调到了Handler那边去

  • frameworks/base/core/java/android/os/Handler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
//msgg.callback是Runnable,如果是post方法则回走这个if
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//回调Handler的handleMessage方法,这里就是为什么要重写handleMessage方法的关键
handleMessage(msg);
}
}

  注意: dispatchMessage()方法针对Runnable的方法做了特殊处理,如果是,则会直接执行Runnable.run()
  Looper.loop()是个死循环,会不断调用MessageQueue.next()获取Message,并调用msg.target.dispatchMessage(msg)回到Handler来分发消息,以此来完成消息的回调。
  loop()方法并不会卡死主线程,但是会阻塞子线程和主线程。Android 是单线程模型, UI的更新只能在主线程中执行, 在开发过程中, 不能在主线程中执行耗时的操作, 避免造成卡顿, 甚至导致ANR。但是这里的死循环(Linux pipe/epoll机制)。Android会在死循环中判断Msg是否为空,如果有Msg则处理,若无则继续死循环(nativepollonce)。子线程之所以能够继续处理任务,是因为从其他线程为其拥有的MessageQueue中通过handler添加了Msg,然而在死循环中的代码看到有Msg不为空了,就会去执行handleMessage。

  主线程与其类似,只是主线程中的Msg,是从系统那边来的+我们自己写的,例如:bind service、activity生命周期等事件发生时,系统都会将对应的Msg(可以参考ActivityThread中的mH对象)发送到对应的进程,这时候Looper.loop()中就会拿到Msg,就会去执行此Msg.target所对应的handler的handleMessage。所以Looper.loop()无论是主线程还是子线程,都会阻塞,但是并不会使系统在死循环卡死。这时候,由于死循环会使cpu忙碌,所以引入了nativepollonce,也就是阻塞的概念。

  • 线程切换调用栈
1
2
3
4
5
6
7
8

Thread.foo(){
Looper.loop()
-> MessageQueue.next()
-> Message.target.dispatchMessage()
-> Handler.handleMessage()
}

  显而易见,Handler.handleMessage()所在的线程最终由调用Looper.loop()的线程所决定。从异步线程发送消息到Handler,这个Handler的handleMessage()方法是在主线程调用的,所以消息就从异步线程切换到了主线程。

Handler的延伸

  由于Handler的特性,它在Android里的应用非常广泛,比如AysncTask、HandlerThread、Messager、IdleHandler和IntentService等等。

IdleHandler

  Android是基于消息处理机制的,比如应用启动过程,Activity启动以及用户点击行为都与Handler息息相关,Handler负责Android中的消息处理,对于Android中的消息处理机制来说,MessageQueue和Looper,Message也是非常重要的对象,而IdleHandler是MessageQueue的静态内部接口。

  IdleHandler,这是一种在只有当消息队列没有消息时或者队列中的消息还没有到执行时间时才会执行的IdleHandler,它存放在mPendingIdleHandlers队列中。

  • frameworks/base/core/java/android/os/MessageQueue.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Line 940

/**
* 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();
}

  从源码的英文定义可以看出,IdleHandler是一个回调接口,当线程中的消息队列将要阻塞等待消息的时候,就会回调该接口,也就是说消息队列中的消息都处理完毕了,没有新的消息了,处于空闲状态时就会回调该接口。

Handler引起的内存泄漏原因以及最佳解决方案

  Handler允许我们发送延时消息,如果在延时期间用户关闭了Activity,那么该Activity会泄漏。这个泄漏是因为Message会持有Handler,而又因为Java的特性,内部类会持有外部类,使得Activity会被Handler持有,这样最终就导致Activity泄漏。解决该问题的最有效的方法是: 将Handler定义成静态的内部类,在内部持有Activity的弱引用,并及时移除所有消息。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static class SafeHandler extends Handler{
private WeakReference<HandlerActivity> ref;

public SafeHandler(HandlerActivity activity){
this.ref = new WeakReference(activity);
}

@Override
public void handleMessage(final Message msg){
HandlerActivity activity = ref.get();

if (activity != null){
activity.handleMessage(msg);
}
}
}

  并且再在Activity.onDestroy()前移除消息,加一层保障:

1
2
3
4
5
@Override
protected void onDestory(){
safeHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}

  虽然这样的双重保障,可以完全避免内存泄漏,但是单纯的在onDestroy移除消息并不保险,因为onDestroy并不一定执行。

为什么我们能在主线程直接使用Handler,而不需要创建Looper?

  前面我们提到了每个Handler的线程都有一个Looper,主线程当然也不例外。通常认为ActivityThread就是主线程。在ActivityThead.main()方法中有如下代码:

  • frameworks/base/core/java/android/app/ActivityThread.java
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
//Line 8145


public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

// Install selective syscall interception
AndroidOs.install();

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

// Call per-process mainline module initialization.
initializeMainlineModules();

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

  • frameworks/base/core/java/android/os/Looper.java # prepareMainLooper()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Line 119

/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

  可以看到在ActivityThread里调用Looper.prepareMainLooper()方法创建了主线程的Looper,并且调用了loop()方法,所以可以直接使用Handler。

  主线程的Looper不允许退出

Handler里藏着的Callback能干什么?

  在Handler的构造方法中有几个要求传入Callback,看看Handler.dispatchMessage(msg)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
//这里的callback是Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果callbcak 处理了该msg并且返回true,就不会再回调handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}

  可以看到Handler.Callback有优先处理消息的权利,当一条消息被Callback处理并拦截(返回true),那么Handler的handleMessage(msg)方法就不会被调用了;如果callback处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被Callback以及Handler处理。我们可以利用Callback这个拦截机制来拦截Handler的消息。场景:在ActivityThread中有个成员变量mH,它是个Handler,又是个极其重要的类,几乎所有的插件化框架都使用了这个方法。

创建Message实例的最佳方式

  由于Handler极为常用,所以为了节省开销,Android给Message设计了回收机制,所以在使用的时候尽量复用Message,较少内存消耗。有两种方法:

  • 通过Message的静态方法Message.obtain();
  • 通过Handler的共有方法handler.obtainMessage();

子线程里弹Toast的正确姿势

  当我们尝试在子线程里直接去弹Toast的时候,会crash:

1
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()  

  本质上是因为Toast的实现依赖于Handler,按子线程使用Handler的要求修改即可,同理的还有Dialog

1
2
3
4
5
6
7
8
9
10
new Thread(new Runnable(){
@Override
public void run(){
Looper.prepare();
Toast.makeText(HandlerActivity.this, "不会崩溃",Toast.LENGTH_SHORT).show();
Looper.loop();
}

}).start();

妙用Looper机制

  我们可以利用Looper的机制来帮助我们做一些事情:

  1. 将Runnable post到主线程执行
  2. 利用Looper判断当前线程是否是主线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final class MainThread{

private MainThread(){

}
private static final Handler HANDLER = new Handler(Looper.getMainLooper());

public static void run(@NonNull Runnable runnable){
if(isMainThread()){
runnable.run();
}else{
HANDLER.post(runnable);
}
}

public static boolean isMainThread(){
return Looper.myLooper() == Looper.getMainLooper();
}
}

Handler的同步屏障

  Handler中的Message可以分为两类:同步消息、异步消息。消息类型可以通过以下函数得知

  • frameworks/base/core/java/android/os/Message.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14

//Line 471
/**
* Returns true if the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
*
* @return True if the message is asynchronous.
*
* @see #setAsynchronous(boolean)
*/
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}

  一般情况下这两种消息的处理方式没什么区别,在设置了同步屏障时才会出现差异。Handler设置同步屏障之后可以拦截Looper对同步消息的获取和分发。加入同步屏障之后,Looper只会获取和处理异步消息。如果没有异步消息会进入阻塞状态。

  • 同步屏障可以通过MessageQueue.postSyncBarrier函数来设置
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
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;

Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}

  可以看到,该函数仅仅是创建了一个Message对象并加入到了消息链表中。但是该Message没有target。通常在使用Handler发送信息时,最终调用enqueueMessage,这里为msg会设置target字段,所以代码层次上来说,同步屏障就是一个target字段为空的Message.

  • 同步屏障的工作原理

  同步屏障只在Looper死循环获取待处理信息时才会起作用,也就是说同步屏障在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
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
@UnsupportedAppUsage
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

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 while循环遍历消息链表
// 跳出循环时,msg指向表头最近的一个异步消息
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) {
// 将msg从消息链表中移除
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;
}

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
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;
}
}

  可以看到,当设置了同步屏障的时候,next函数将会忽略所有的同步消息,返回异步消息。同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息,只处理异步消息。

  • 发送异步消息,在创建Handler时使用一下构造函数中的一种(async=true)
1
2
3
4
public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

  • 同步屏障的应用

  Android 4.1之后增加了Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制时机。ViewRootImpl的requestLayout开始绘制流程:

1
2
3
4
5
6
7
8
9
10
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();//检查是否在主线程
mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
//重要函数
scheduleTraversals();
}
}

  Androoid应用框架为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//设置同步障碍,确保mTraversalRunnable优先被执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//内部通过Handler发送了一个异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

  mTraversalRunnable调用了performTraversals执行measure、layout、draw,为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障。

总结

  1. Handler的背后有Looper、MessageQueue支撑,Looper负责消息分发,MessageQueue负责消息管理
  2. 在创建Handler之前一定需要先创建Looper
  3. Looper有退出的功能,但是主线程的Looper不允许退出
  4. 异步线程的Looper需要自己调用Looper.myLooper().quit退出
  5. Runnable被封装进了Message,可以说是一个特殊的Message
  6. Handler.handlerMessage()所自爱的线程是Looper.loop()方法被调用的线程,也可以说称Looper所在的线程,并不是创建Handler的线程
  7. 使用内部类的方式Handler可能会导致内存泄漏,即便在Activity.onDestory里移除延时消息,必须要写成静态内部类。

  最近两次面试都会问到Handler机制以及原理,在使用过程中没有详细了解其源码以及原理,这里做简单的笔记整理,豁然开朗,有很多之前没有注意的问题,借此好好整理。