Handler

103 阅读5分钟

1、Handler成员介绍

我们先来介绍一下组成Handler机制的几位成员,以及它们常用的方法

1.1 Handler类

Handler类是应用程序开发的入口,在消息队列机制中,扮演着生产者的角色,同时还肩负着消息执行者的重担,常用的方法有:

方法名称说明
sendMessage()系列发送普通消息、延迟消息,最终调用queue.enqueueMessage()方法将消息存入消息队列
post()系列提交普通/延迟Runnable,随后封装成Message,调用sendMessage()存入消息队列
dispatchMessage(Message msg)分发消息,优先执行msg.callback(也就是runnable),其次mCallback.handleMessage(),最后handleMessage()

1.2 Looper类

Looper在消息队列机制中扮演消费者的角色,内部持有共享的消息队列,其本质是封装对消息队列的操作,常用的方法只有两个:

方法名称说明
prepare()创建消息队列
loop()遍历消息队列,不停地从消息队列中取消息,消息队列为空则等待

1.3 MessageQueue类

实际的共享消息队列,提供保存和取出消息的功能,底层由链表实现,常用方法就一个:

方法名称说明
next()获取消息,三种情况 1. 有消息,且消息到期可以执行,返回消息 2. 有消息,消息未到期,进入限时等待状态 3. 没有消息,进入无限期等待状态,直到被唤醒

1.4 Message类

消息的承载类,使用享元模式设计,根据API不同缓冲池大小也不同,API 4时缓冲池大小为10,常用方法:

方法名称说明
obtain()系列获取一个消息实例
recycle()回收消息实例

2、主线程为什么不用初始化Looper?

答:因为应用在启动的过程中就已经初始化主线程Looper了。

每个java应用程序都是有一个main方法入口,Android是基于Java的程序也不例外。Android程序的入口在ActivityThread的main方法中:

public static void main(String[] args) {
    ...
 // 初始化主线程Looper
    Looper.prepareMainLooper();
    ...
    // 新建一个ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    // 获取ActivityThread的Handler,也是他的内部类H
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    ...
    Looper.loop();
 // 如果loop方法结束则抛出异常,程序结束
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

main方法中先初始化主线程Looper,新建ActivityThread对象,然后再启动Looper,这样主线程的Looper在程序启动的时候就跑起来了。我们不需要再去初始化主线程Looper。

4、Handler机制Looper死循环为什么不会导致应用卡死

1)来看loop()循环。通过一个for()无限循环从MessageQueue.next()取消息,然后调用Handler的dispatchMessage(msg)把消息传递过去。

public static void loop() {
    final Looper me = myLooper();//1. 首先是获取当前线程的Looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//2. 取出当前线程Looper中存放的MessageQueue
    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    for (;;) { // 这里会无限循环
    //3. 从MessageQueue中取消息,没有消息会被阻塞的,导致线程休眠。收到消息的时候唤醒
        Message msg = queue.next(); 
        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
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        //4. 注意啦,这里开始分发当前从消息队列中取出来的消息,回调给Handler
        msg.target.dispatchMessage(msg);

        msg.recycleUnchecked();
    }
}

2)接下来看下MessageQueue的next方法( 当MessageQueue中无消息时,调用nativePollOnce方法进入阻塞状态,主线程会释放CPU资源,进入休眠状态。下次收到消息MessageQueue的enqueueMessage方法调用nativeWake重新唤醒线程)

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
    // 1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。 
    // 2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。 
    // 3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时)
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        //当消息队列为空时,这里会导致阻塞,直到有消息加入消息队列,才会恢复
        //这里是native方法,利用的是linux的epoll机制阻塞
        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;
            /如果target==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息。同步屏障就起到了一种优先级的作用
            if (msg != null && msg.target == 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;
            }
            .....
        }
       ......
    }
}

那这里为什么没有导致ANR或者程序死循环呢?

当MessageQueue中无消息时,queue.next()会阻塞在nativePollOnce(),这里涉及到Linux pipe/epoll机制,nativePollOnce()被阻塞时,主线程会释放CPU资源,进入休眠状态. 直到下个消息到达或者有事务发生,会通过pipe管道写入数据来唤醒主线程工作,就可以继续工作了.

这里主线程进入休眠状态和死循环是有区别的.

死循环是指主线程死锁在这里,一直执行某一块代码,无法再响应其他事件.

休眠状态是指在内核状态里,主线程被挂起,线程状态转移到休眠状态.