本文进一步分析Handler和Looper的源码

1.Looper
从上文我们大概了解了ThreadLocal是什么,而Looper之所以能保障一个线程只能有一个Looper,也是借助ThreadLocal来实现的。
Looper类在加载时就自动生成一个threadLocal对象,也就是说一个looper对象对应一个threadLocal

1
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper.prepare()源码如下:

1
2
3
4
5
6
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));
}

PS:根据sThreadLocal.set(new Looper(quitAllowed)); 得知,Looper.prepare()其实就是建立了一个新的looper
从上面得知,looper.prepare()先通过threadLocal.get(),源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }    
    return setInitialValue();
}

即通过当前线程维护的threadLocalMap来获取Entry,如果能够得到这个Entry对象,说明这个threadLocal已经赋值过,即已经给当前的threadLocal赋值过looper了,所以不能再给线程设置looper。这也是为什么一个线程只有一个looper。

首先Thread里面有个ThreadLocalMap来保证当前线程跟ThreadLocal类型的值一一对应。Looper里面有个泛型为Looper的ThreadLocal对象,当Looper在prepare的时候,会先从当前线程的threadLocalMap找有没有存在这个ThreadLocal值,如果有,说明该线程已经赋值了一个Looper,此时会报错,如果没有则会把当前线程跟泛型Looper的ThreadLocal保存到当前线程的ThreadLocalMap里面。

再看上面的threadLocal.set()源码:

1
2
3
4
5
6
7
8
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

用到了set()证明那个threadLocal还没有赋值过,所以需要在当前线程的threadLocalMap中赋值Entry,即存储threadLocal以及对应的值。

综上,looper.prepare()就是把线程跟looper一起绑定。

分析looper.loop();源码

1
2
3
4
final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}

首先myLooper()是通过threadLocal来获取到looper对象。

1
2
3
4
5
6
7
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

接着获取到looper对应的messageQueue。在looper的构造函数中,会新建一个messageQueue对象。

1
2
3
4
5
6
final MessageQueue queue = me.mQueue;

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

然后死循环不断获取message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for (;;) {
Message msg = queue.next(); // 从messagequeue中获取下一个消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
……
try {
//通过msg.target去分发消息
//剧透:msg.target就是handler,后面解释
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

//回收消息,划重点,后面再解释
msg.recycleUnchecked();
}

也就是说直到messageQueue.next()为空,即没有消息了,才会结束死循环,即looper.loop()运行结束。

2.handler
handler在创建一个新对象时,会找到当前线程的looper和对应的messageQueue,如果找不到,则会报错说没有先调用Looper.prerare()。然后再获取到对应的looper的messageQueue。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public Handler(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;
}

PS:之前一直只在Activity中写Handler handler = new Handler()而没有调用Looper.prepare()却可用,是因为Activity里面有ActivityThread,而ActivityThread的main()里面调用了Looper.prepareMainLooper(); 所以其实Activity用的就是MainLooper主线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//ActivityThread.java
public static void main(String[] args) {
……

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

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
//Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

如果是在子线程使用handler,在子线程一定要调用Looper.prepare把子线程和looper绑定,然后才能新建一个handler对象,最后通过looper.loop()来循环消息列表。用法示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//todo
}
};
handler.sendEmptyMessage(MSG_SEND);
Looper.loop();
}
};

通过Handler如何实现线程的切换?
比如要在子线程中更新UI的话,只能拿主线程的looper来构建handler并做相应的操作

1
2
3
4
5
6
handler_main = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
helloTextView.setText("getMainLooper");
}
};

一切信息分发从handler.sendMessage或者handler.post开始。而一切可追踪至handler.sendMessageAtTime()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

前面在分析looper时,看到是由msg.target去分发消息,而在上面的代码也可以看到msg.target其实就是Handler,所以在消息分发处理就跳到了当前的handler。

先看分发消息部分,把大概流程理一遍

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

在dispatchMessage这里涉及到三种处理消息的方法
①msg.callback不为空时,根据msg.callback去处理:

1
2
3
private static void handleCallback(Message message) {
    message.callback.run();
}

而msg.callback一般是通过Hanlder的postXXX(Runnable r)传入的runnable对象来处理

1
2
3
4
5
6
7
8
9
public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

②mCallback不为空时,就通过mCallback的handleMessage()
Handler内部有一个callback,可以通过这个callback来处理消息:

1
2
3
4
5
6
7
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}

这个callback可以在构造handler时传入,其使用方法大概如下:

1
2
3
4
5
6
Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});

③如果msg没有callback并且handler也没有callback,那就用handler自己的handleMessage()方法去处理消息。

1
2
3
4
5
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

其使用方法如下,也就是一般的使用方法:

1
2
3
4
5
6
Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

Handler的post与sendMessage的区别:
由上可知,post里面有个runnable,所以消息最后是在post里面的runnable执行。send的话最后是根据handler的callback或者handleMessage去执行。
ps:message.callback.run();看似是在新的子线程中执行,但是直接调用线程的run方法相当于是在一个普通的类调用方法,还是在当前线程执行,并不会开启新的线程。
最终总结:

  1. post和sendMessage本质上是没有区别的,只是实际用法中有一点差别
  2. post也没有独特的作用,post本质上还是用sendMessage实现的,post只是一中更方便的用法而已。post比send的方便在于post不用去判断msg的类别,直接处理。

ok,再看回刚刚的enqueueMessage方法,发现最后是queue.enqueueMessage,于是又回到了MessageQueue那里。
PS:MessageQueue是一个单链表的数据结构,提供了一个next()方法获取下一个消息
既然是链表结构,那么在入队时是否有什么规律呢?
答案是:有!而且是按照时间顺序的。
所以如果当我们调用postDelay方法时,也是因为按时间顺序入队列,可以让消息晚点处理。
看一下enqueueMessage的方法

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
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//PS:如何保证队列不混乱(如何保证线程安全)?
//通过synchronized来保证了线程的安全性。
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

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;
//从这里开始
if (p == null || when == 0 || when < p.when) {
// messageQueue为空,或者触发事件最早.
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.
// 换句话说,这里是遍历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;
}

从之前的loop可以看到会死循环走queue.next方法,现在看一下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
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();
}
// 阻塞nextPollTimeoutMillis这么长的时间
// 该方法将调用 native void nativePollOnce(long, int), 该方法将一直阻塞直到添加新消息为止.
// 当将 Message 添加到队列时, 框架调用 enqueueMessage 方法, 该方法不仅将消息插入队列,
// 而且还会调用native static void nativeWake(long)唤醒
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;
}
……
}
}

也就是说,消息的定时发送的原理是,延迟处理某个消息而已。阻塞一定的时间后再处理该消息
nextPollTimeoutMillis如果为-1,则表示无限等待,直到被唤醒为止。如果值为0,则无需等待立即返回。
总而言之,消息循环时,会判断是否有消息,没有消息就把nextPollTimeoutMilis设置为-1,进入休眠,直到被唤醒。
那MessageQueue#next 在没有消息阻塞的时候,如何恢复?


    nativePollOnce在当前没有消息要分发处理的时候,阻塞线程,以下三种情形会唤醒线程:
  • 方法内部出现异常和错误

  • 超时了,即延时消息的触发时间到了

  • 有新的消息插入消息队列(插入同步或异步消息,同步屏障除外)

3.什么是同步消息、异步消息、消息屏障
在handler的enqueueMessage时,我们看到有个标记是否是异步消息,然后msg.setAsynchronous(true);
异步消息是不能使用的,因为相关设置都是hide。所以正常情况下我们发送的消息都是同步消息。
消息屏障也是一种消息,但它是没有target的,也就是没有对应的handler。它的作用就是阻塞同步消息,优先处理异步消息。
异步消息在Android系统的使用是用来更新Ui的。为了让View能够有快速的布局和绘制,插入一个同步屏障后,同步消息全都阻塞住无法执行,只能执行更新ui的异步消息,以免影响到ui绘制。
在发送异步消息的时候向消息队列投放同步屏障对象,消息队列会返回同步屏障的token,此时消息队列中的同步消息都会被暂停处理,优先执行异步消息处理,等异步消息处理完成再通过消息队列移除token对应的同步屏障,消息队列继续之前暂停的同步消息处理。

屏障消息(同步屏障有以下特征):
①没有给target赋值,即不用handler分发处理,后续也会根据target是否为空来判断消息是否为消息屏障
②消息队列中可以插入多个消息屏障
③消息屏障也是可以有时间戳的,插入的时候也是按时间排序的,它只会影响它后面的消息,前面的不受影响
④消息队列中插入消息屏障,是不会唤醒线程的(插入同步或异步消息会唤醒消息队列)
⑤插入消息屏障后,会返回一个token,是消息屏障的序列号,后续撤除消息屏障时,需要根据token查找对应的消息屏障
⑥发送屏障消息的API被隐藏,需要通过反射调用postSyncBarrier方法

原理,看上面的MessageQueue的next方法,有这么一段

1
2
3
4
5
6
7
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());
}

也就是当遇到了同步屏障时,会一直不断找异步消息来处理。

4.什么是IdleHandler?
IdleHandler可以解释为,当handler消息机制在出现空闲时,可以“抽空”来处理IdleHanlder的事件。
它长这样子:

1
2
3
4
5
6
7
8
9
10
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();
}

只有一个queueIdle方法需要实现,返回值为false代表只执行一次
至于使用方法,等讲完原理后附上android的使用例子

关于原理,大概流程是这样的:
1.如果本次循环拿到的Message为空,或者!这个Message是一个延时的消息而且还没到指定触发时间,那么就认定当前的队列为空闲时间。
2.接着会遍历mPendingIdleHandlers数组(这个数组里面的元素每次都会到mIdleHandlers中去拿)来调用每一个IdleHandler实例的queueIdle方法。
3.如果这个方法返回false的话,那么这个实例就会从mIdleHandlers中移除,也就是当下次队列空闲的时候,不会继续回调它的queueIdle方法了。
4.执行完后会再次进入循环,然后进入休眠,等到下个消息的插入来唤醒
上源码:

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
Message next() {

// 当第一次走next会设置为-1,注意了!!
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// 尝试获取message,如果找到符合条件的就返回
……

// 能走到这里,说明没有找到符合的msg
// 所以只有当queue为空或者当前message是延迟处理才会运行
// 第一次运行,获取mIdleHandlers的个数
// 由于后面pendingIdleHandlerCount不可能<0,所以只会走一次
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有idle handlers,阻塞等待
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
// 最多4个
mPendingIdleHandlers = new MessageQueue.IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// 运行idle handlers.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final MessageQueue.IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
// idler的返回值
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
// 返回值为false,移除
mIdleHandlers.remove(idler);
}
}
}

// 重设为0
pendingIdleHandlerCount = 0;

// 当执行完idleHandler时需要设置为0
nextPollTimeoutMillis = 0;
}
}

那么,当idleHandles不为空时,是不是就会一直执行idleHandlers呢?
不会!
假设,第一次调用next时,pendingIdleHandlerCount设为-1。
开始进入死循环,这时刚好没有消息处理,那么就会for循环执行所有的idleHandles,保留那些queueIdle返回true的。
最后我们会把pendingIdleHandlerCount和nextPollTimeoutMillis都设为0。
好的然后再次进入循环,记住pendingIdleHandlerCount和nextPollTimeoutMillis都是0(pendingIdleHandlerCount设为-1是在循环前)。然后刚好没有任何消息,nextPollTimeoutMillis设为-1。继续往下走,就会进入这里

1
2
3
4
5
if (pendingIdleHandlerCount <= 0) {
// 没有idle handlers,阻塞等待
mBlocked = true;
continue;
}

走了continue,说明又开始循环了。
这次nextPollTimeoutMillis为-1了,所以执行到nativePollOnce(ptr, nextPollTimeoutMillis)会被阻塞住了。也就不会执行idleHandles了。

等到下一个消息进入,唤醒,执行消息后,当再次调用next时,如果此时又没有消息需要处理了,就会执行之前剩下的idleHandles了。

系统源码的使用例子:当AMS调用了doLowMemReportIfNeededLocked,会触发到ActvitityThread的processInBackground,这个方法会发出GC_WHEN_IDLE的消息。ActivityThread在收到GC_WHEN_IDLE的消息时,会添加一个gcIdler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ActivityThread.java
case GC_WHEN_IDLE:
scheduleGcIdler();
break;
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}

final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
return false;
}
}


    IdleHandler适用于非必要且不耗时的ui更新操作等(主线程)
    其他使用场景:
  • Activity启动优化(加快App启动速度):onCreate,onStart,onResume中耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间

  • 想要在一个View绘制完成之后添加其他依赖于这个View的View,当然这个用View#post()也能实现,区别就是前者会在消息队列空闲时执行

  • 发送一个返回true的IdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作

  • 一些第三方库中有使用,比如LeakCanary,Glide中有使用到,具体可以自行去查看

5.Message对象创建的方式有哪些 & 区别?
两种构建方法:Message.obtain() 和 new Message()
obtain:如果没有message,则new Message对象。如果有,则复用之前的Message对象
源码解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 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是一个单链表结构。所以sPool本质是一个单链表结构,obtain最终就是从这个单链表结构不断拿到缓存的值。

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
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;

public Messenger replyTo;
public int sendingUid = -1;

/*package*/ static final int FLAG_IN_USE = 1 << 0;

/** I set message is asynchronous */
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

/** Flags to clear in the copyFrom method */
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

/*package*/ int flags;

/*package*/ long when;

/*package*/ Bundle data;

/*package*/ Handler target;

/*package*/ Runnable callback;

// 重点看这里!!
// next获取它的下一个消息
/*package*/ Message next;

ok,从上面看到是通过sPool是否为空来判断怎么拿缓存的,那么sPool又是从哪里赋值呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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++;
}
}
}

是在recycleUnchecked的时候赋值的,那recycleUnchecked又是什么时候调用呢?

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 (;;) {
// 具体过程可以看上面分析,这里不再赘述
……
// 可以看到在循环获取消息时
// 最后一步是把消息回收了
msg.recycleUnchecked();
}
}

于是从这里可以得出,当消息队列还没有消息时,会先新建一个Message对象,然后通过enqueue进入队列,通过looper.loop拿出第一个消息来处理后,把该消息给回收了,如果这时候又要发送第二个消息时,就可以直接复用刚刚的sPool,即第一个message的对象。而且因为在recycler时会清空所有的what、callback这些信息,也不怕有脏数据。

obtain在获取时会判断当前消息池是否有可以复用,如果没有就新建对象作为链表头,如果有就直接复用链表头,移除,然后把下一个消息作为链表头。当拿出去的消息对象处理完回收时,则又把消息放进链表池里面。本质就是获取移除表头,回收加入表尾。
整个流程大致如下:
假设同时有三个消息A、B需要发送,当A执行完毕时,执行recycle方法:
next = sPool;
sPool = this;
sPoolSize++;
即A.next = sPool = null; sPool = this = A; 此时的缓存消息链表:A->null
B处理完毕执行recycle后:B.next = sPool = A; sPool = this = B。 消息链表:B->A->null
这时调用了Message.obtain,返回了sPool,就是B了

总结:这就是通过obtain方法获取Message对象的详情。通过obtain方法获取Message对象使得Message到了重复的利用,减少了每次获取Message时去申请空间的时间。同时,这样也不会永无止境的去创建新对象,减小了Jvm垃圾回收的压力,提高了效率。

6.Looper的quit和quitSafely区别
本质都是调用了messagequeue的quit(boolean safe)

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
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}

synchronized (this) {
if (mQuitting) {
return;
}
// 设置mQuitting为true,这样在next时因为这个为true而return null
mQuitting = true;

if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}

// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}

private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}

private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}

quit()实际上是把消息队列全部清空,然后让MessageQueue.next()返回null令Looper.loop()循环结束从而终止Handler机制,但是存在着不安全的地方是可能有些消息在消息队列没来得及处理。而quitsafely()做了优化,该方法仅仅会清空MessageQueue消息池中全部的延迟消息。并将消息池中全部的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发全部的非延迟消息。

这两篇handler的文章是之前在学习过程中做的笔记,我再拿出来整理一遍。由于学习过程中也是看了很多网上的博客,当时也没有记录。所以如有博主觉得侵权或需要做注释的可以联系我,我会补上原文链接和说明。
如果这两篇文章存在哪些错误,也请各位大佬直接评论,防止我误导了别人,后续我也会修改的。