前言
Handler
在 Android
中的地位不用多说了,没有消息机制则寸步难行。
通常我们会使用 Handler
在主线程与子线程之间通信,那么它们是怎么通信的,有什么玄机?
本文会带你从 Java
层的源码了解其原理,以及在使用过程中要注意的地方。下一篇则从 Native
层了解背后的原理。
Handler
我们从构造方法开始看起。
构造方法
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
26
27
28
|
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());
}
}
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;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
|
对于没有 Looper
作为参数的构造方法,会使用当前线程里的 Looper
, Callback
则会在消息分发的时候用到,最后一个 async
用来表示是否是异步消息。
发送消息
使用 Handler
发送的消息大多都会调用 sendMessageAtTime
方法,除了 sendMessageAtFrontOfQueue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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);
}
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
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, 0);
}
|
不管是哪个方法,最后都是调用 enqueueMessage
把消息添加到消息队列中。
其中 uptimeMillis
是以开机时间为起点的运行时间,这样做就可以避免系统时间被改了导致消息执行错乱。
1
2
3
4
5
6
|
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
|
enqueueMessage
1
2
3
4
5
6
7
8
9
10
11
|
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// msg 中的 target 就是 handler ,这样在消息分发的时候就知道交给哪个 handler 处理了。
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) { // 判断是否是异步消息
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); // 加入队列
}
|
在最后一行调用 MessageQueue.enqueueMessage
加入到消息队列中。
移除消息
1
2
3
4
|
public final void removeMessages(int what) {
// 调用 MessageQueue 的 removeMessages
mQueue.removeMessages(this, what, null);
}
|
Handler
中的 removeMessages
会调用 MessageQueue
中的 removeMessages
,后面再分析 MessageQueue
中是怎么移除的。
Handler 中的消息分发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
|
- 首先会判断
Message
的 callback
是否为空,如果不为空,则调用 handleCallback
,而 handleCallback
则直接调用 message.callback.run()
- 当
Message
的 callback
为空并且成员变量 mCallback
不为空的时候,调用 mCallback.handleMessage
。 mCallback
在构造的时候被赋值。
- 如果
Message
的 callback
和 mCallback
都没有的话,则调用 handleMessage
,这里边是个空实现,也就是什么都不做。通常需要我们自己覆写该方法,一般我们使用的就是这个。
获取Message
1
2
3
4
|
public final Message obtainMessage()
{
return Message.obtain(this);
}
|
obtainMessage
也有许多重载的构造方法,不过都大同小异,都是调用的 Message.obtain
。
避免内存泄漏
先来看一个常用的写法
1
2
3
4
5
6
7
|
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
|
这样的写法是有问题的,会导致内存泄漏。这里的 Handler
是个内部类,会默认持有外部类的引用,也就是 Activity
。
当 Activity
finish 的时候 ,这里的 Handler
往 MessageQueue
中插入的消息是延迟执行的,而 MessageQueue
的生命周期和整个应用程序的生命周期一样长,所以 MessageQueue
中的 Message
不会被回收,从而导致 Message
中的 Handler
也不会被回收。
Handler
不会回收则 Activity
也不会被回收。所以 Activity
在需要销毁的时候发现还有一个 Handler
在引用它,这就会导致 Activity
不能被垃圾回收器回收,从而导致内存泄漏。
使用静态内部类
要解决 Handler
内存泄漏可以使用静态内部来实现,因为静态内部类不持有外部类的引用。
但是使用了静态内部类不会持有外部类(Activity),所以我们要把外部类(Activity)的实例传进去,并且使用弱引用包裹外部类对象,这样在外部类被销毁之后,只要发生一次 GC
就可以回收外部类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private static class MyHandler extends Handler {
private WeakReference<Activity> mWeakReference;
public MyHandler(Activity activity) {
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Activity activity = mWeakReference.get();
// do something
}
}
|
具有感知生命周期的Handler
除了使用静态内部类的方式,我们还可以结合 LifeCycle
组件,让 Handler
具有感知生命周期的能力,从而在 Activity
销毁的时候,自动移除掉 Handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class LifecycleHandler extends Handler implements LifecycleObserver {
private LifecycleOwner lifecycleOwner;
public LifecycleHandler(final LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
lifecycleOwner.getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private void onDestroy() {
removeCallbacksAndMessages(null);
lifecycleOwner.getLifecycle().removeObserver(this);
}
}
|
前面说了会导致内存泄漏是由于消息队列里的消息持有 Handler
导致 Activity
在销毁的时候不能被垃圾回收器回收,如果我在 Activity
销毁的时候,把 Handler
相关的消息都移除是不是就不会导致泄漏了。
移除之后,消息队列里的消息就不会持有 Activity
中 Handler
了,这样就能保证 Handler
能够被回收,也就可以保证 Activity
可以被回收了。
Callback 的运用
在消息分发一节讲到,当 msg.callback
为空的时候会优先调用 Handler
的成员变量 mCallback
,并且 mCallback
是个有返回值的方法,返回 true
表示消费这个消息,返回 false
则不消费这个消息,还可以继续交给 handleMessage
处理。
这里处理方式和事件分发机制差不多。
所以我们可以在这里对消息进行拦截,甚至可以让消息被处理两次,不过一般不会这么用。不过在插件化中使用到对消息进行加工处理,我们看看VirtualAPK是怎么运用的。
com/didi/virtualapk/PluginManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
protected void hookInstrumentationAndHandler() {
try {
ActivityThread activityThread = ActivityThread.currentActivityThread();
Instrumentation baseInstrumentation = activityThread.getInstrumentation();
final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation);
Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
// 通过反射给 Handler 的 mCallback 赋值
Reflector.with(mainHandler).field("mCallback").set(instrumentation);
this.mInstrumentation = instrumentation;
} catch (Exception e) {
Log.w(TAG, e);
}
}
|
在上面的代码中发现通过反射把 VAInstrumentation
赋给了 mCallback
,按照我们的猜测它应该需要实现 Handler.Callback
接口,我们跟进去看看
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
|
// 实现了 Handler.Calllback
public class VAInstrumentation extends Instrumentation implements Handler.Callback {
// 重写了 Handler.Callback 的 handleMessage 方法
@Override
public boolean handleMessage(Message msg) {
if (msg.what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg.obj;
try {
Reflector reflector = Reflector.with(r);
Intent intent = reflector.field("intent").get();
intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
ActivityInfo activityInfo = reflector.field("activityInfo").get();
if (PluginUtil.isIntentFromPlugin(intent)) {
int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
if (theme != 0) {
Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + " after :0x" + Integer.toHexString(theme));
activityInfo.theme = theme;
}
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
return false;
}
}
|
不出所料, VAInstrumentation
确实是实现了 Handler.Callback
,并且在 handlerMessage
中对 what
为 LAUNCH_ACTIVITY
进行了处理。主要做了如下两件事
- 给
Intent
设置了宿主的 ClassLoader
这样才能找到这个类,然后通过反射创建出 Activity
。
- 使用插件的
theme
替换 ActivityInfo
的 theme
。
Looper
关于 Looper
我们从程序的最开始看起,程序的入口是 main
方法,在 Android
中,我们并不需要自己写 main
方法,因为 Android
已经帮我们写好了。
Android
中的 main
方法在 ActivitThread.java
中。我们的 Looper
就在这里初始化的,来进去探索一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
// ...
}
|
在这里调用来 prepareMainLooper
和 loop
创建了 Looper
对象并开启了主线程循环。我们一个一个来看。
prepareMainLooper
1
2
3
4
5
6
7
8
9
10
11
|
public static void prepareMainLooper() {
// 这里传递了 false 表示不会退出 Looper
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 获得刚刚创建的 Looper 保存到 sMainLooper
sMainLooper = myLooper();
}
}
|
prepare
1
2
3
4
5
6
7
8
|
private static void prepare(boolean quitAllowed) {
// 确保 Looper 在一个线程中只会被创建一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 把创建好的 Looper 放到 ThreadLocal ,是线程私有的,只有当前线程才能访问,这样就不会存在共享的问题
sThreadLocal.set(new Looper(quitAllowed));
}
|
Looper
1
2
3
4
5
6
|
private Looper(boolean quitAllowed) {
// 创建消息队列
mQueue = new MessageQueue(quitAllowed);
// 获取当前线程
mThread = Thread.currentThread();
}
|
Looper
的构造方法被设置为 private
,所以不能通过 new
来创建,只能通过 prepare
来创建 Looper
对象。
在 Looper
中还创建了 MessageQueue
并且保存了当前线程。
myLooper
1
2
3
|
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
|
从本地线程中取出 Looper
loop
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
|
public static void loop() {
final Looper me = myLooper(); // 获取 Looper
final MessageQueue queue = me.mQueue; // 获取 Looper 中的消息队列
for (;;) {
Message msg = queue.next(); // 获取消息队列的 Message ,可能会阻塞
if (msg == null) {
return;
}
// 打印消息,调试的时候使用
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 进行消息分发
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 回收消息,放入回收池,避免频繁创建和回收有利于提高性能。
msg.recycleUnchecked();
}
}
|
在 ActivityThread
中的 main
方法最后就是调用 Looper.loop
。而在 loop
中有一个死循环,一直在处理消息队列中的事件,这样程序就不会退出。
loop
会一直从消息队列中取消息,如果没有消息则会阻塞,直到有消息唤醒,取出消息之后则通过 Handler
进行消息分发,然后处理消费掉这个消息。
最后在处理完这个消息之后,会调用 recycleUnchecked
对消息进行回收,以便下次进行使用。
quit
1
2
3
4
5
6
7
|
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
|
quit
和 quitSafely
最后都调用了 MessageQueue.quit
,只是参数不同,如果传递的是 true
则表示移除尚未触发的消息,而 false
则移除所有消息。
MessageQueue
根据前面的探索,可以知道 MessageQueue
对象的创建是在 Looper
的构造方法中。依照国际惯例,我们从 MessageQueue
的构造函数开始看起。
构造方法
1
2
3
4
5
6
|
private native static long nativeInit();
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
|
在构造方法中传递进来一个参数 quitAllowed
,表示是否可以退出,在主线中这个是 false
。
接着通过 JNI
调用 nativeInit
初始化消息队列。有关 native
的,我们放到后边再说。
存消息
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
|
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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 {
// 根据阻塞,消息队列的第一个元素是否是barrier, 而且是异步消息来判断是否需要唤醒,如果这个异步消息不是第一个,后面会设置为 false 不需要唤醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 根据事件顺序,找到一个合适位置
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
// p 此时并不是第一个异步消息,所以不需要唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
// 唤醒
nativeWake(mPtr);
}
}
return true;
}
|
取消息
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
|
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 只有第一次循环是 -1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 判断是否存在同步屏障,同步屏障 target 为 null
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 {
// 取出消息并从队列移除
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 {
// 没找到合适的消息
nextPollTimeoutMillis = -1;
}
// 消息正在退出
if (mQuitting) {
dispose();
return null;
}
// 没有消息或者时间还没到则计算 IdleHandler 的个数, 只有第一次循环会小于0
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 如果没有 IdleHandler 则阻塞,并循环等待
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 遍历 IdleHandler 执行 queueIdle, 只有第一次循环才会执行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
// 空闲时执行
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 返回 false 则移除 IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 置为0,第二次遍历就不会执行
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
|
这里需要注意的是 nativePollOnce
在没有消息的时候会阻塞,但是并不是非阻塞的情况下就一定又消息可以处理,因为在 native
层又消息可以处理也会导致 nativePollOnce
返回,这时候 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
|
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// 从头部移除连续的满足条件的 Message
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// 移除剩下满足条件的 Message
while (p != null) { // 找到尾巴为止
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
// 这里采用的是把后一个指针往前挪,而不是指针往后移动,
// 与平时写法不太一样,习惯了往后移动的写法,一时间没看出来
p.next = nn;
continue;
}
}
p = n;
}
}
}
|
移除消息这里存在一个可以优化的地方,在同一类消息发送的很频繁的时候,会存在许多大量重复的消息,而且这些消息所做的事情都是一样的,这时候我们只需要保留一个最新的就可以了。
1
2
3
4
|
// 把未处理的都移除掉
handler.removeCallbacksAndMessages(msgType);
Message msg = handler.obtainMessage(msgType);
handler.sendMessage(msg);
|
同步屏障
先来说一说同步屏障,同步屏障存在的目的是为了快速响应异步消息,使得异步消息的处理优先级比同步消息高,通常消息屏障会用在屏幕绘制上,这样才能保证不卡顿。
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
|
private int postSyncBarrier(long when) {
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) {
// 不是队列的头部
msg.next = p;
prev.next = msg;
} else {
// 放到队列的头部
msg.next = p;
mMessages = msg;
}
return token;
}
}
|
这里的 Message
没有对 target
进行赋值,也就是没有 Handler
,会被认定为同步屏障。
异步消息处理的时间是同步屏障的时间,在 next
方法中我们提到过,在寻找消息的时候发现消息队列的队首是个同步屏障的消息,就会从中找到一个时间最少的异步消息进行执行。
这时候就不是异步消息自己的时间被执行了。
源码中使用同步屏障的例子
ViewRootImpl.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
|
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 插入同步屏障,保存同步屏障的token,可用于移除同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 通过token移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 连续调用多次 invlidate 会移除掉之前的,这样就可以减少绘制的次数,在一次Vsync 信号中没必要绘制太多
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
|
可以看到在进行屏幕刷新的时候会使用到同步屏障,这样就能够保证屏幕能够即使响应,而不会导致卡顿。
移除同步屏障
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
|
public void removeSyncBarrier(int token) {
synchronized (this) {
Message prev = null;
Message p = mMessages;
// 找到 target 为空, token 相等的 Message
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
// 从消息队列中移除
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
|
同步屏障会影响同步消息处理的优先级对于异步消息没有任何差别。
反射调用同步屏障
同步屏障是个隐藏的 API
我们不能直接使用,需要通过反射的方式来进行调用
1
2
3
4
5
6
7
8
9
|
public void postSyncBarrier() {
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
token = (int) method.invoke(Looper.getMainLooper().getQueue());
}
public void removeSyncBarrier() {
Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
method.invoke(Looper.getMainLooper().getQueue(), token);
}
|
Handler 中的使用
在 Handler
中的源码中我们会发现构造函数带有 async
参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@UnsupportedAppUsage
public Handler(boolean async) {
// ...
}
@hide
public Handler(@Nullable Callback callback, boolean async) {
// ...
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
// ...
}
|
会发现这些带有 async
参数的构造方法不是被标记为 @hide
就是别标记为 @UnsupportedAppUsage
,我以我们是无法直接使用的。
可以使用的情况下给 async
赋值为 true
,这样这个 Handler
所发送的消息就都是异步的了。
IdleHandler
在取消息的时候我们聊到了,在线程空闲的时候第一次遍历会调用 IdleHandler.queueIdle
,我们可以在这个时候做一些额外的操作,准备一些资源啥的,来看看怎么使用
添加 IdleHandler
1
2
3
4
5
6
7
|
Looper.getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
// 做一些不耗时的工作
return false;
}
});
|
在 queueIdle
中返回 true
执行完后不会被移除,返回 false
则会被移除
移除 IdleHandler
1
|
Looper.myQueue().getQueue().removeIdleHandler(mIdleHandler);
|
如果在 queueIdle
中返回的是 true
则 IdleHandler
的移除需要自己来做,通过 removeIdleHandler
来移除。
dispose
1
2
3
4
5
6
|
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
|
调用 nativeDestroy
和 nativeInit
一样通过 JNI
操作,首尾呼应。
Message
成员变量
变量 |
作用 |
what(int) |
消息类型 |
when(long) |
消息触发时间 |
arg1(int) |
携带的参数1 |
arg2(int) |
携带的参数2 |
obj(Object) |
消息的内容,可以理解为携带较复杂的参数 |
target(Handler) |
消息的处理者 |
callback(Runnable) |
用于处理消息的回调 |
消息的创建就是给上面这些成员变量赋值。
消息的回收利用
我们知道消息是一个传建和销毁非常频繁的对象,如果每次用到都去创建的话,那么虚拟机会频繁的回收和创建对象,有可能造成内存抖动,进而影响性能。
所以对于这么频繁使用的对象,我们可以把它们缓存起来,用到的时候直接拿来用,这样就方便快多了。
创建
先来看看创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
private static Message sPool;
private static int sPoolSize = 0;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next; // 从头部取出一个缓存对象
m.next = null; // 断开和池子的关系
m.flags = 0; // 清除使用标识
sPoolSize--; // 池子的可用数量减1
return m;
}
}
// 池子里没有数据,创建一个新对象
return new Message();
}
|
obtain
中从池子的头部取走一个 Message
对象,如果池子里没有缓存对象则创建一个新的。
回收
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
|
public void recycle() {
if (isInUse()) { // 判断消息是否还在使用
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
@UnsupportedAppUsage
void recycleUnchecked() {
// 清空消息里的数据,并把 flags 设置为 FLAG_IN_USE
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) {
// 判断是不是超出池子的最大大小,超过了就不放入池子中,池子最大可以存放 50 个
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
|
Message
的主要作用是用于存放数据,所以没有太多复杂的操作,算是消息机制中最简单的了。
对于类似消息这种频繁创建和删除的对象,我们也可以学习使用这种池子的模式来缓存对象,可以避免内存抖动,提高性能。
总结
这里借用一下Android消息机制1-Handler(Java层) - Gityuan博客 | 袁辉辉的技术博客这篇文章的图片来展示一下整个消息机制

- 我们通过
Handler
发送消息到 MessageQueue
Looper
就像一个引擎一样,不断的从 MessageQueue
里边取出 Message
交给 Handler
处理
Message
承载我们要传递的数据
MessageQueue
则是沟通 Message
、 Looper
以及 Handler
的桥梁,让它们可以很好的协作。
参考