我会通过讲解 Handler/Looper/MessageQueue/Message 这几个类的作用以及它们之间的协作,来简单的描述 Android 的消息机制。
1. Message
我们说的消息机制中的消息就是 Message 这个类
1.1 消息的组成
消息的标识
- int what: Message 对象的唯一标识
消息携带的一些值
- int arg1: 用来存储一些 integer 值,是 Bundle 的低成本替代方案
- int arg2: 用来存储一些 integer 值,是 Bundle 的低成本替代方案
- Object obj: 一个任意类型的对象
- Bundle data: Bundle 数据
如何处理消息
- Runnable callback: 一段可执行代码块,如果消息对象携带 callback 属性,则优先执行 callback 代码块
- Handler target: 用来处理消息,可以通过重写 Handler 对象的 handleMessage 来实现具体的处理逻辑
除此之外,Handler 中也提供了另外一种对消息对象的处理,之后我们会说到。
其他属性
- long when: 消息期望被处理的时间
- Message next: 指向下一个消息对象,用来支持链表
- int flags: 标志位,用来用来标识消息是否为异步,是否在使用中
static final int FLAG_IN_USE = 1 << 0;
static final int FLAG_ASYNCHRONOUS = 1 << 1;
可以看到,最低位为 1 表示消息在使用中,倒数第二位为 1 表示当前消息是异步消息
1.2 如何创建一个消息对象
Message 提供了一个无参的构造函数来创建 Message 对象
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
不过官方更推荐使用 obtain()
方法来创建 Message 对象,我们来看看几个重载的 obtain
方法
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
上面列出的两个重载的方法,都是先通过无参的 obtain()
放来来创建一个 Message 对象,然后在给对应的属性赋值,还有一些多参的重载方法我没有列出,它们的步骤和上述两个方法做的事情大致相同。
消息池
接着看这个 obtain()
方法是如何创建消息对象的:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
可以看到,Message 内部用链表实现了一个全局池子,每次调用 obtain()
方法会从池子中捞取一个消息对象(链表头),如果消息对象为空,则通过构造函数来创建一个消息对象并返回。
- static Message sPool: 消息列表的头节点
- static int sPoolSize = 0: 当前消息池的大小
- private static final int MAX_POOL_SIZE = 50: 默认消息池的大小
消息回收
既然可以从消息池中取消息,那消息是如何放进池子中的(消息回收)?
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it " + "is still in use.");
}
return;
}
recycleUnchecked();
}
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 = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
- 检查消息标记位,不在使用状态时,可以被回收
- 将标记为设置为使用中状态,同时将其他属性设置为初始化值
- 回收池不满的话,则放入回收池(头插法)
1.3 一些问题
看完上述对于 Message 的介绍之后,你可能能够回答这些问题?
- 创建一个 Message 对象的推荐做法
- Message 回收池的数据结构
也可能会有这些疑问?
- 异步消息有什么用?
在之后我们将介绍异步消息的作用。
2. 一个小例子
我们带着 Looper 和 Handler 创建有没有依赖关系? 这个问题来看这个例子
package com.xiezhen.handlerstudy;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView tvHandler;
private Button btnUpdate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvHandler = (TextView) findViewById(R.id.tv_handler);
new Thread(new Runnable() {
@Override
public void run() {
new Handler();
}
}).start();
}
}
运行上面的代码, 程序将直接崩溃,错误日志如下:
在一个没有调用 Looper.prepare() 方法的线程中无法创建 Handler,也就是说 Handler 必须在 Looper 线程中创建和使用。
3. Looper
我们来看下源码中的示例代码:
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();
}
}
- 先调用 Looper.prepare() 方法创建一个 Looper 对象
- 创建 Handler 对象,并且重写 handleMessage 方法来处理输入的消息
- 调用 Looper.loop() 方法开启消息循环,该方法内会不断的取出消息然后进行分发处理
3.1 Looper.prepare()
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));
}
无参的 prepare()
方法会调用 prepare(true)
方法,表示不允许退出消息队列,false 则表示允许退出。
之后会调用 Looper 的构造函数 new Looper(quitAllowed)
来创建一个 Looper 对象,并且存储在 ThreadLocal 中(在此之前,我们先会判断当前线程有没有创建过 Looper 对象,一个线程只允许创建一个 Looper 对象)
有关于 TreadLocal 的介绍请看这里 Android的消息机制之ThreadLocal的工作原理
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper 的构造函数比较简单
- 创建一个 MessageQueue 对象并且赋值给 mQueue 变量
- 获取当前线程对象并赋值给 mThread 变量
3.2 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;
}
// 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);
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
msg.recycleUnchecked();
}
}
首先会检查当前 ThreadLocal 中有没有设置过 Looper 对象,没有的话则会抛出异常,在调用 loop()
方法之前必须调用 prepare()
方法。
然后开启一个循环:
- 通过
queue.next()
方法从 MessageQueue 中取出消息 - 通过设置的 Printer 打印 Dispatching 日志
- 调用
msg.target.dispatchMessage(msg)
进行消息处理 - 通过设置的 Printer 打印 Finished 日志
- 回收消息
3.3 检测耗时的消息
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
在 loop 方法的循环中,在处理消息前后都会通过 Printer 打印日志,我们可以自己实现 Printer 并且设置给 Looper,来统计每个消息的处理耗时(参考 BlockCanary 的实现)
package com.example.realxz.handlertest
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.support.v7.app.AppCompatActivity
import android.util.Printer
import android.widget.Toast
const val THRESHOLD_MILLIS = 3000
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val obj = object : IBlockListener {
override fun doOnBlock() {
Toast.makeText(this@MainActivity, "Blocking", Toast.LENGTH_SHORT).show()
}
}
val printer = TestPrinter(obj)
val mainLooper = Looper.getMainLooper()
mainLooper.setMessageLogging(printer)
val handler = Handler(mainLooper)
handler.post {
Thread.sleep(5000)
}
}
class TestPrinter(private var listener: IBlockListener) :Printer {
private var printStarted = false
private var startTime: Long = 0
override fun println(x: String?) {
if (!printStarted) {
startTime = System.currentTimeMillis()
printStarted = true
} else {
val endTime = System.currentTimeMillis()
printStarted = false
if (isBlock(endTime)) {
listener.doOnBlock()
}
}
}
private fun isBlock(endTime: Long) = (endTime - startTime) > THRESHOLD_MILLIS
}
interface IBlockListener {
fun doOnBlock()
}
}
- 自定义 Printer 实现 println 方法,来统计 dispatchMessage 消耗的时间,如果超过我们设置订的时间(3 秒钟)则会触发我们的回调函数
- 获取当前线程的 Looper 对象,并将自定义的 Printer 对象设置给 Looper 对象
- 发送一个消息,让线程休眠 5 秒钟来模拟耗时操作
- 执行上述代码,你将会看到 Blocking 的 Toast 提示
3.4 小结
打个比方,prepare 方法相当于创建了一个行李传送履带,loop 方法相当于启动行李传送履带,行李相当于我们发送的 Message,looper 会将最先放置在履带上的行李传送到终点进行处理。
4. Handler
对于 Handler 来说,我们需要关注的三个点是:
- 如何创建 Handler 对象
- 如何发送消息
- 如何处理消息
4.1 创建 Handler 对象
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我们最常使用的无参构造函数,默认回调为空,并且发送的消息全部为同步消息,可以看到 Handler 的创建是依赖于 Looper 对象的。
同时我们也可以使用指定的 Looper 对象来创建 Handler:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
4.2 使用 Handler 发送消息
首先看看我们最常用的 sendMessage(Message msg)
方法的调用链:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终我们会调用 MessageQueue 的 enqueueMessage(Message msg, long when)
方法来完成消息的入队,可以想象是在传送带上放一个行李,实际上是一个单链表的插入操作。
- 将 this(Handler 对象)赋值给 msg.target 属性
- 根据创建 Handler 时传入的 mAsynchronous 标志,来决定消息是否异步
- 根据当前系统时间插入到链表中
4.3 处理消息
在介绍 Looper 的时候,在 loop 方法的 for 循环中获取到消息后,会调用 msg.target.dispatchMessage(msg)
方法进行消息处理,根据上面的描述,可以知道 msg.target 是一个 Handler 对象,我们看下 Handler 的 dispatchMessage(Message msg)
方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
- 如果 msg.callback 不为空,则执行 callback 的
run()
方法,这类消息通常使用post(Runnable r)
方法创建,会将 Runnable 对象赋值给 Message 的 callback 属性 - 如果 Handler 的 mCallback 对象不为空,则执行 Callback 对象的
handleMessage
方法,Callback 对象通过 Handler 的构造函数传入 - 实现一个 Handler 子类,并重写
handleMessage(Message msg)
方法来对消息进行处理
5. MessageQueue
先回顾一下文章的前半部分说了什么:
- Message 的数据结构
- Message 的复用机制
- Looper 中通过 MessageQueue 的
next()
方法来获取消息 - Looper 中通过 Message 的 target 属性来处理消息
- Looper 中在处理消息前后打印日志,统计消息处理的耗时
- Handler 中通过 MessageQueue 的
enqueueMessage(Message msg, long when)
方法根据when
指定的时间,将 Message 插入链表合适的位置
最开始介绍 Message 的时候,我们已经知道消息内部有个 Message 类型的属性 next,用来支持链表。
Message 的回收池使用的链表这种数据结构,MessageQueue 当中同样是使用链表来存储消息:
Message mMessages
MessageQueue 内部的 mMessages 属性指向列表的头节点。
接下来重点放到消息的存放和取出上
5.1 enqueueMessage(Message msg, long when)
根据 when
指定的时间,将 msg 插入到链表合适的位置上
boolean enqueueMessage(Message msg, long when) {
// 注意点1
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) {
// 注意点2
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 注意点3
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 {
// 注意点 4
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;
}
// 注意点 5
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
- 首先检测消息的 target 和 flags 属性,不允许将 target 为空或者被标记为正在使用的消息通过 enqueueMessage 方法入队
- 如果当前队列已经标记为退出,则将消息回收,入队失败
- 满足以下三种情况的话,会将消息插入链表头部
- 当前链表为空(p == null)
- 当前消息需要立刻执行(when == 0)
- 当前消息早于根节点消息的时间(when < p.when)
- 链表的比较操作,根据 when 值将链表插入合适的位置
- 如果 needWake == true,则唤醒消息队列
- 消息入队成功
消息的入队其实就是单链表的插入操作,不难理解,整个插入过程中会对 needWake 这个值进行修改,来决定是否唤醒消息队列(消息队列在没有消息的时候处于阻塞状态)
- 插入链表头部
- 消息队列阻塞,needWake = true
- 消息队列不阻塞,needWake =false
- 插入列表中
- 头节点消息 target == null,并且当且入队消息是第一个异步消息,needWake = true
之前我们说过通过 enqueueMessage 方法插入的消息都会检查 target 属性是否为空,如果为空则抛出异常,那么这种 target == null 的消息是怎么插入链表中的呢?
5.2 postSyncBarrier()
MessageQueue 内部为我们提供了一个方法 postSyncBarrier
方法
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 对象,并且根据时间插入到了链表合适的位置上,这个消息的插入不会唤醒消息队列。
这个消息我们称为是同步屏障,它的作用是拦截所有屏障之后的同步消息,异步消息则不受影响(我认为在这种情况下,异步消息的优先级提高了)
同时记得需要调用 removeSyncBarrier(int token)
方法来删除同步屏障,不然我们的同步消息就无法执行了
5.3 next()
最后看看,如何从消息队列中取出消息,我删除了部分代码,保留了一些关键信息
Message next() {
int pendingIdleHandlerCount = -1; // IdleHandler 数量
int nextPollTimeoutMillis = 0; // 下次读取消息的时间
for (;;) {
//注意点 1
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
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;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
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);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
-
nativePollOnce(ptr, nextPollTimeoutMillis);
方法是一个阻塞操作(管道机制),当队列中没有消息的时候,或者没有消息现在需要处理的时候,代码会阻塞,这意味着 nativePollOnce 方法之后的代码不会执行(我理解阻塞是当前线程不再占用 CPU 资源)。 - nextPollTimeoutMillis 默认值为 0 ,意味着 nativePollOnce 方法不会阻塞,后续代码得到执行,等于 -1 的话意味着 nativePollOnce 方法会阻塞,等待着管道的写入信号,写入这一操作其实就是在介绍 enqueueMessage 时讲到的
nativeWake(mPtr)
方法, - 然后开始从链表中取出消息对象
- 首先判断当前消息是不是一个同步消息屏障,如果遇到了同步消息屏障,则循环去找链表中第一个异步消息
- 找到异步消息后,根据时间判断当前是否需要执行异步消息,满足时间条件话,返回消息对象
- 如果异步消息不满足时间条件,则会将时间差值赋值给 nextPollTimeoutMillis 变量,在下次循环时,nativePollOnce 方法将会阻塞对应的时长
- 如果是同步消息,依然是对时间进行判断,需要执行的话,则返回消息对象,不然则会在下次循环的时候阻塞对应的时间
- 如果当前消息队列中没有消息,或者第一个消息对象还没到执行时间,我们会执行 IdleHandler 相关的处理,IdleHandler 的作用就像是它的名字一样,会在消息队列空闲时执行一些操作
- 如果当前消息队列没有设置 IdleHandler 则进行下次循环,并且设置 mBlock = true
- 如果当前消息队列的 IdleHandler 列表中有值,则会遍历 IdleHandler 列表,执行 IdleHandler 的
queueIdle()
方法 - 将 pendingIdleHandlerCount 置为 0,这意味 IdleHandler 在
next()
方法中只会执行一次 - 将 nextPollTimeoutMillis 设置为 0,下次循环调用 nativePollOnce 方法的时候不会阻塞,代码会继续执行去消息队列中取消息,这是因为在 IdleHandler 执行的过程中可能会有新的消息传递进来。
5.4 异步消息的用法
在我们刷新 UI 的时候会调用 ViewRootImpl
类中的 scheduleTraversals
方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
注意在该方法中通过 mHandler.getLooper().getQueue().postSyncBarrier();
在消息队列中设置了一个同步消息屏障,接着在 Choreographer
类中通过发送异步消息来完成一些绘制操作
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
通过这种方式来保证绘制的优先级,在绘制完毕后会删除同步消息屏障
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
5.5 IdleHandler 的实际运用
ActivityThread 的 Handler 对象 mH 中会对 what == 120 的消息做如下处理
public static final int GC_WHEN_IDLE = 120;
case GC_WHEN_IDLE:
scheduleGcIdler();
break;
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
当收到 120 的消息后,会在消息队列中加入一个名叫 mGcIdler 的 IdleHander 对象,从名字上看是做一些 GC 操作
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
// + "m now=" + now);
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc("bg");
}
}
从代码上看,确实是做了一些 GC 操作,并且是在消息队列空闲的时候执行。
6. 小结
阅读完文章后你应该能够了解
- Handler 如何发送消息
- Looper 如何取消息
- MessageQueue 的消息出队、入队操作
- 同步消息屏障和异步消息的作用
- IdleHandler 有什么用
- Message 的结构和复用
- 如何统计消息处理的耗时