Android 消息机制 Handler

602 阅读5分钟

1、消息机制的基本原理

5cffc92f07d7374338

各平台的消息机制的原理基本上都是相近的,其中一些主要概念大概有

  • 消息发送者:消息的发送方,有时也是消息的处理方

  • 消息队列:存放消息的地方

  • 消息循环器:消息的调度者,用于不断从消息队列中取出消息,分发给处理者

2、Android中的消息机制

Android中的消息机制主要用于线程间的通讯,主要的流程见下图

5cff39675de1090796

3、消息机制的初始化过程

3.1 Handler的创建

Handler对象在创建时会保存当前线程的looper和MessageQueue

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        //1.创建Looper对象
        Looper.prepare(); 
        //2.完成Handler、Looper、MessageQueue彼此的绑定
        mHandler = new Handler() { 
            @override
            public void handleMessage(Message msg) {
                //todoSomething
            }
        //3.开始死循环    
        Looper.loop(); 
}}
public Handler(Callback callback, boolean async) {
        ....
        //get当前线程关联的Looper对象
        mLooper = Looper.myLooper(); 
        if (mLooper == null) {
            throw new RuntimeException(}
        mQueue = mLooper.mQueue; //MessageQueue
        ...
    }
3.2 Looper的创建

Looper的创建相对简单,创建完会存到对应的线程中

public final class Looper

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

    private static void prepare(boolean quitAllowed) {
        ...
        sThreadLocal.set(new Looper(quitAllowed));
    }

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

    public static Looper myLooper() {
        return sThreadLocal.get();
    } 
}    
public class ThreadLocal<T> {

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            return e.value
        }
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
}

class Thread implements Runnable {     
    ...
    //真正存储线程copy的地方
    ThreadLocal.ThreadLocalMap threadLocals = null; 
}    

3.3 MessageQueue的创建

Looper对象的初始化方法里,首先会新建一个MessageQueue对象。MessageQueue对象的初始化方法通过JNI初始化C++层的NativeMessageQueue对象。并将引用地址返回给Java层保存在mPtr变量中,通过这种方式将Java层的对象与Native层的对象关联在了一起。

public final class MessageQueue {
    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);

    //单链表的数据结构来存储消息列表 
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
}

4、消息传递的过程

5d008d090cf8067103

主流程:

  • Handler通过sendMessage()发送Message到MessageQueue队列,队列中按message需要处理的时间排序;

  • Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target Handler来处理;

4.1发送消息
  1. handler发送消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}
  1. 消息插入到消息队列,按when顺序插入到链表中
boolean enqueueMessage(Message msg, long when) {
        synchronized (this) { //避免多线程同时发消息的并发问题
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            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 {
                // Inserted within the middle of the 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;
            }
            if (needWake) { 
                nativeWake(mPtr); //以触发nativePollonce方法结束阻塞
            }
        }
        return true;
    }
4.2 取出消息

同一片Looper不断循环取出Message:

首先通过调用Looper的loop方法开始消息监听。loop方法里会调用MessageQueue的next方法。next方法会堵塞线程直到有消息到来为止。

public static void loop() {
    final Looper me = myLooper();  //获取TLS存储的Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; ....");
    }
    //获取Looper对象中的消息队列
    final MessageQueue queue = me.mQueue;  

    ...

    for (;;) { //进入loop的主循环方法
        Message msg = queue.next(); //可能会阻塞 May be block
        if (msg == null) { //没有消息,则退出循环
            return;
        }
        msg.target.dispatchMessage(msg); //用于分发Message
        ...
        msg.recycleUnchecked();  //将Message放入消息池
    }
}

MessageQueue.next()

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        //用来保存注册到消息队列中的空闲消息处理器(IdleHandler)的个数,当消息队列当线程发现它的消息队列没有新的消息需要处理时,不是马上就进入睡眠等待状态,而是先调用注册到它的消息队列中的IdleHandler对象的成员函数queueIdle,以便它们有机会在线程空闲时执行一些操作。
        int pendingIdleHandlerCount = -1; // -1 only during first iteration

        //用来描述当消息队列中没有新的消息需要处理时,当前线程需要进人睡眠等待状态的时间, -1,表示无限等待,直到被其它线程唤醒;
        // 0,继续循环检查队列;
        // >0,以nextPollTimeoutMillis的毫米数等待
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands(); //处理binder进程间通讯
            }

            //for循环不断地调用成员函数nativePollOnce来检查当前线程的消息队列中是否有新的消息需要处理。如果有消息需要处理,则将msg赋给mMessages并返回,否则就一直阻塞
            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.
                    //target为空,则为阻塞message,要找到下一个异步的message
                    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. and return
                        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;
                }
                
                if (mQuitting) {
                    dispose();
                    return null;
                }

                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; 

                boolean keep = false;
                try {
                    keep = idler.queueIdle(); //执行idler对应的任务
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            pendingIdleHandlerCount = 0;.
            nextPollTimeoutMillis = 0;
        }
    }

next方法通过调用nativePollOnce方法来监听事件。next方法内部逻辑如下所示(简化):

  1. 进入死循环,以参数timout=0调用nativePollOnce方法。

  2. 如果消息队列中有消息,nativePollOnce方法会将消息保存在mMessage成员中。nativePollOnce方法返回后立刻检查mMessage成员是否为空。

  3. 如果mMessage不为空,那么检查它指定的运行时间。如果比当前时间要前,那么马上返回这个mMessage,否则设置> timeout为两者之差,进入下一次循环。

  4. 如果mMessage为空,那么设置timeout为-1,即下次循环nativePollOnce永久堵塞。 nativePollOnce方法内部利用epoll机制在之前建立的管道上等待数据写入。接收到数据后马上读取并返回结果。

参考资料:

IdleHandler mp.weixin.qq.com/s/KpeBqIEYe…

Android消息机制:zhoujinjian.cc/2017/08/01/…