消息处理机制

简单一句话:

Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message, 再有Handler进行Message的分发和处理.

来张动图:

应用开始

从整个应用开始来看,Looper先开始准备,然后调用loop()方法

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
....
 Process.setArgV0("<pre-initialized>");
    // Lppper开始准备
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    // attach方法其实在thread里面便会创建一个Binder线程
    // 这也就是为什么死循环后程序还能运作的原因
    thread.attach(false);

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

    if (false) { // 条件是false进不来
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // Loop 开始循环,主线程进入了死循环
    Looper.loop();

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

由此而来,我们一般会有2个问题:

(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?

知乎上的回答(十分赞):点这里

有了这个引入后,接下来将会从Looper,Handler,Message,MessageQuene逐个分析

Looper

一般一个线程就对应一个Looper

重要成员有:

1
2
3
4
5
6
7
8
9
// ThreadLocal使得每个Thread都有一个自己的副本
// 这样就可以保证每个线程中都有一个自己的Looper,不会被别的线程共享
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主线程中的Looper
private static Looper sMainLooper;  // guarded by Looper.class
// MessageQueue 消息队列用作保存和取出Message
final MessageQueue mQueue;
// 当前线程
final Thread mThread;

从开始我们可以先从Looper.prepareMainLooper()方法下手

其实就是调用了Looper中的prepare方法,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// quitAllowed
// 是否允许退出,这里主线程的时候是传入false,
//最后会在MessageQuene中的quit方法中抛出异常,从而不让退出
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
//sThreadLocal 就像一个Map,一个新建的Looper给放进去
//类似一个线程对应一个Looper
    sThreadLocal.set(new Looper(quitAllowed));
}

这里就到了Looper的构造方法:

1
2
3
4
5
6
// 这里把之前的quitAllowed给传进了MessageQueue;
// 并且mThread是当前的线程
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

接下来就是核心方法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
29
30
31
32
33
34
public static void loop() {
    // 获取loop和queue
    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;

    //主要是为了确保是否还是当前进程
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) { // 这里就是死循环了
        // 取出msg,如果为空,return掉
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

       ...
        // target 就是 Handler,这里就会调用Handler的dispatchMessage方法发送消息
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        // 将Message回收到消息池,下次要用的时候不需要重新创建,obtain()就可以了。
        msg.recycleUnchecked();
    }
}

这里有个问题,主线程是UI线程和用户交互的线程,优先级应该很高,主线程的死循环一直运行是不是会特别消耗CPU资源吗?App进程的其他线程怎么办?

关键就是queue.next()方法里,如果此时没有消息,就会阻塞在这个方法里面。这时候主线程会释放CPU资源进入休眠状态,直到有下个消息进来时候就会唤醒主线程

.2 版本以前,这套机制是用我们熟悉的线程的wait和notify 来实现的,之后的版本涉及到Linux pipe/epoll机制,通过往pipe管道写端写入数据来唤醒主线程工作。原理类似于I/O,读写是堵塞的,不占用CPU资源。

MessageQuene

先看下构造方法:

1
2
3
4
5
6
7
 MessageQueue(boolean quitAllowed) {
    // 之前的是否允许退出
    mQuitAllowed = quitAllowed;
    // 可以理解为当前队列是否还有没效,0就是没,大于0就是有
    // 后面的dispose方法就是让其无效,从而退出程序
    mPtr = nativeInit();
}

继续刚才,进入到queue.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
Message next() {

    final long ptr = mPtr;
    if (ptr == 0) { // 队列无效,返回空退出程序
        return null;
    }

    int pendingIdleHandlerCount = -1;
    int nextPollTimeoutMillis = 0;

    // 又来一个死循环了
    for (;;) {

        ...

        // 该方法会阻塞队列
        // 1.没消息的时候
        // 2.nextPollTimeoutMillis>0,而且等待时间还没到
        nativePollOnce(ptr, nextPollTimeoutMillis);

        // 返回继续执行
        synchronized (this) {

            final long now = SystemClock.uptimeMillis();

            Message prevMsg = null;
            Message msg = mMessages;
            // 如果mMessages当前消息不为空,handler不为空,也不是异步消息,
            // 把当前消息返回
            if (msg != null && msg.target == null) {
                // 否则一直查找下去,直到是一条target不为空也不是异步的消息
                // 如果是异步消息,则跳出循环
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            // 如果mMessages不为空
            if (msg != null) {
                if (now < msg.when) {
                    // 时间还没到,要等下次,经常用的postDelay,
                    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 {
                // 没有消息 ,重新把时间变成-1
                nextPollTimeoutMillis = -1;
            }
    }
}

这里就把queue.next()给分析完了,那问题是mMessages又是如何得到呢?

这里就是Handler发送消息的时候使用了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
58
59
boolean enqueueMessage(Message msg, long when) {
    // 如果target为空,或者消息正在使用 都抛出异常
    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) {
        // 如果消息队列正在退出,回收消息
        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;
        }

        // 消息标记为使用中 flags |= FLAG_IN_USE
        msg.markInUse();
        msg.when = when; // 消息发送时间
        Message p = mMessages; //当前消息
        boolean needWake; //是否需要唤醒
        // 当前没有消息需要执行或者当前的消息执行时间比msg要大,说明,msg应该第一个执行
        if (p == null || when == 0 || when < p.when) {
            // 当前mMessages变为要msg
            msg.next = p;
            mMessages = msg;
            // mBlocked 是否正在阻塞中,如果是,需要唤醒
            needWake = mBlocked; 
        } else {
            // 如果消息是异步的,p又没有target,而且还在阻塞中,变为要唤醒
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 在当前消息队列中查找是否有消息比这个msg应该提前执行的,
            // 如果有就不执行
            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;
        }

        // 如果需要唤醒,则唤醒,next方法()
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

Message

消息,类似一个Model一样,主要是一些信息

里面有几个重要参数

1
2
3
4
5
6
// 锁对象
private static final Object sPoolSync = new Object();
// 消息池,就像一个链表一样,指向当前的消息
private static Message sPool;
// 消息池的大小
private static int sPoolSize = 0;

主要方法:obtain(),这里也就对应了之前为什么在Loopr最后调用msg.recycleUnchecked();

因为Message类可以看成是个单例模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 没什么好说的,就是从消息池中获取一个消息,如果没有,就new一个
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();
}

当前消息变为新的消息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public static Message obtain(Message orig) {
    Message m = obtain();
    m.what = orig.what;
    m.arg1 = orig.arg1;
    m.arg2 = orig.arg2;
    m.obj = orig.obj;
    m.replyTo = orig.replyTo;
    m.sendingUid = orig.sendingUid;
    if (orig.data != null) {
        m.data = new Bundle(orig.data);
    }
    m.target = orig.target;
    m.callback = orig.callback;

    return m;
}

Handler

Handler算是我们平时用的最多一个类

几个重要参数:

1
2
3
4
final Looper mLooper; // 所在的Looper,默认主线程
final MessageQueue mQueue; // 所在消息队列
final Callback mCallback;  // callBack 最后回调 handleMessage 方法
final boolean mAsynchronous; // 是否异步 传给了消息的 mAsynchronous

我们一般使用都会重写该handleMessage方法

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

这里对于dispatchMessage方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 public void dispatchMessage(Message msg) {
    // 消息的callback不为空 , 执行消息的
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // callback 不为空 执行 callback
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 最后才执行
        handleMessage(msg);
    }
}

在看下平时经常用的几个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.
    public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

2.
    public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}
3.
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
4.
    public final boolean post(Runnable r)
{
    // getPostMessage 也就是消息的构建一个callback = r 的消息
   return  sendMessageDelayed(getPostMessage(r), 0);
}

都会调用了sendMessageDelayed方法:

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

最终调用了enqueueMessage方法,触发queue.enqueueMessage:

1
2
3
4
5
6
7
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; // 这里赋予target
    if (mAsynchronous) { //如果是异步的话
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

结尾

至此,已经大致把整个消息机制基本流程分析了一遍,当然里面还有很多细节没有分析, 当然你也可以看看HandlerThread类,在研究下