捋一下Handler机制的过程.

整个机制主要涉及到四个类:Handler、MessageQueue、Message、Looper.

首先ActivityThread启动时,会调用prepareMainLooper(),这个方法会初始化主线程的Looper,保存在sThreadLocal中,每个线程只有唯一的Looper。

Looper初始化的时候,会新建一个MessageQueue(消息队列)。

接下来就是Looper.loop()。主要代码如下:

public static void loop() {

    final Looper me = myLooper();
        
    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;
        }


        msg.target.dispatchMessage(msg);
        //..................
    }
}
复制代码

loop函数是个死循环,不断调用queue.next()接收消息并处理,如果没有消息时会阻塞,有消息的时候会调用dispatchMessage,这里的msg.target就是发送消息的handler。

再说说Handler。send和post方法最终都是进入sendMessageAtTime中,然后调用enqueueMessage,将message放到MessageQueue中。Looper的loop再循环取出执行,最终,Handler形成了一个循环:Handler->MessageQueue->Message->Handler。

FAQ:

Q:既然loop是个死循环,queue.next()会阻塞,那岂不是主线程也会卡死了?

A:因为Android应用是由事件驱动的。looper.loop()不断的轮询接收事件,handler不断的处理事件。每一个点击触摸事件,或者Activity的生命周期的创建,都是运行在looper.loop()控制之下,简单的来说就是,这些都是属于一个事件消息,然后由looper.loop()轮询到,接着做相应的操作。也就是说,这些这些事件都在looper.loop()循环内执行的。这些事件消息的处理,可能会卡顿,造成了looper.loop()循环的阻塞,而不是looper.loop()阻塞了这些事件。真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

Q:主线程的死循环一直运行是不是特别消耗CPU资源呢?

A:其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

Q:如果有个延迟消息导致代码被阻塞了,后面继续添加非延迟消息怎么办?

A:Handler。send和post方法最终都是进入sendMessageAtTime中,然后调用enqueueMessage,enqueueMessage部分代码如下:

msg.when = when;
//这是一个根据when排序的链表,when最短的排在最前面
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
    // New head, wake up the event queue if blocked
    // 发现新插入的消息不是延迟消息,或者比mMessages里面的when还要小那么就插入到mMessages最前面,然后可能是等待Looper再次轮询,或者需要唤醒阻塞的线程
    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.
    //这里就是遍历mMessages把新添加的Message根据when插入到一个合适的位置(调整next的指向)
    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);
}
复制代码

Looper 获取到 延迟消息后会判断多久之后执行,如果没到时间就是阻塞(休眠)线程,这个时候如果 MessageQueue 添加进一个 非延迟消息,会在添加进队列的时候进行排序插入到最前面,然后唤醒线程,这个线程执行后续代码遍历拿到刚刚添加最上面的非延迟消息执行…

转载于:https://juejin.im/post/5cdf8a9fe51d4510905a1156

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值