Handler消息机制

前言

提到handler 首先我们会想到他是用来线程之间通讯的,把子线程中的数据发送到主线程当中去做ui的更新。对于handler可以从handler, Message,MessageQueue以及loop这几个方面去讨论。

Handler

对于经常使用到handler的同学都知道 这个handler一般会创建在主线程,但handler也可以创建在子线程当中,当然如果直接创建的话会loop的空指针错误,具体为什么会报错到loop的时候我们再详细讲解。 hanlerd在发送消息的时候用到两种方式 一种是post 一种是sendMessage 。这两种都研究过会发现 post的内部还是通过send去发送这个消息。
在这里插入图片描述
上图是一个简单的handler发送消息的过程。查看源码可以看到 handler.sendMessage->sendMessageDelayed->sendMessageAtTime->enqueueMessage->MessageQueue.enqueueMessage到这里这个message消息才传到到MessageQueue里面。

MessageQueue

MessageQueue 是一个什么东西 他有什么意义?
MessageQueue是消息队列,相当于是一个容器,在创建looper时,同时创建的MessageQueue对象。

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

MessageQueue里面包含了大量的message对象 当用message对象来的时候就存入 是以链表的形成存入,同时他也是无规则,无顺序的物理结构。当有message消息来了之后 代码如下:

boolean enqueueMessage(Message msg, long when) {
        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;
            }

            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.  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.
                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;
    }

Message

对呀message的理解就简单多了 ,他实现Parcelable的接口,起到对数据传递的作用。需要注意点的是:
obtain和直接new Message()的区别
消息recycle()后,会被缓存到静态变量sPool中。spool是一个单链表。使用obtainxxx()会直接从sPool中取第一个message来复用。避免了重复创建过多消息对象。
代码如下:

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();
    }`

Looper

到了最重要的一个环节 looper。对于looper来说他的意义就是在不停的循环 拿取消息 将消息发送到handler当中。
在ActivityThread中创建

public static void main(String[] args) {Looper.prepareMainLooper();Looper.loop();
}

在创建的时候在 Looper.prepareMainLooper();中调用prepare()创建Looper对象 同时创建messageQueue对象
所以在一个线程当中之能有一个looper 和messageQueue对象 可以有多个handler对象。对个handler对象公用一个looper和messageQueue对象。
looper里面还包含一个死循环 那么这个死循环 会不会造成应用的卡死?会一直消耗cpu吗?
首先第一点应用会不会卡死 卡死就是anr(application not responese)了呗 。对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程肯定不能运行一段时间后就自动结束了,那么如何保证一直存活呢??简单的做法就是可执行代码能一直执行下去,死循环便能保证不会被退出,例如:binder线程也是采用死循环方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单的死循环,无消息时会休眠,但是死循环又如何处理其他事物呢??通过创建新的线程。真正卡死主线程操作的是在回调方法onCreate、onStart、onResume等操作时间过长,会导致掉帧甚至ANR,Looper.loop()本身不会导致应用卡死。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值