Handler机制(深入篇)

Handler机制(深入篇)

反思

通过上一节 “handler驱动模型",我们已经明白。Android系统,任何事件(小到滑动,大到 界面绘制)等,都是基于Looper 不停的循环来完成的。那么问题来了:

  1. 当MessageQueue中,没有待处理的Message时,Looper还是一直进行着循环,那是不是很消耗cpu资源呢?同理,主线程也存在MainLooper,为什么UI线程并不会卡死呢?
  2. MessageQueue 中,存在着系统相关的Message 和 非系统相关的Message。如果仅仅按照处理时间的优先级,去处理消息的话。那么当Message数量达到一定的级别的话,必然会带来UI绘制卡顿,和掉帧。那么Android系统又是通过什么机制来解决这种问题呢?

epoll 模型(Question 1)

epoll 是一种,Linux系统吓得I/O 复用模型。他是一种阻塞式轮询
简单的理解,

  1. 就是当内核缓冲区没有数据写入的时候,处理读出数据的线程就会阻塞调,并掉出了系统的调度队列,暂时不会去瓜分CPU宝贵的时间片了。
  2. 当内核缓冲区,有数据写入的时候,内核同样会发送事件,唤醒 处理读出数据的线程,重新开始工作。

Looper 机制中,底层就是epoll 模型,通过 navive层方法,达到了这种阻塞式 循环。从而解决了我们遇到的第一个问题。

//MessageQueue.java
public final class MessageQueue {

  boolean enqueueMessage(Message msg, long when) {
     ...
      // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
            // 这里唤醒 nativePollOnce 的沉睡
                nativeWake(mPtr);
            }
 }

 Message next() {
            ...
         for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //nativePollOnce 这里陷入沉睡, 等待唤醒
            nativePollOnce(ptr, nextPollTimeoutMillis);
            ...
            }

}

nativePollOnce :方法用于“等待”, 直到下一条消息可用为止. 如果在此调用期间花费的时间很长, 则您的主线程没有实际工作要做, 而是等待下一个事件处理.无需担心
nativeWake: MessageQueue 利用名为 epoll 的 Linux 系统调用,当有新的可用Message消息加入的时候。会调用 nativeWake 等待,然后, 内核从等待状态中取出 epoll 等待线程, 并且该线程继续处理新消息

同步屏障(Question2)

简单的理解:是用来分割同步消息和异步消息。它的实际载体是一种 没有持有Hander 的Message.

Message 分为同步 Message 和 异步Message。是通过 FLAG_ASYNCHRONOUS 来标识。一般,我们发送的Message都是 同步消息。而异步Message ,是由系统在特定情况下发出。

//Handler.java
public class Handler{

public Handler() {
        this(null, false);
    }
public Handler(Looper looper) {
        this(looper, null, false);
    }
//所有的构造函数都会走到这个重载方法
 public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

 //Message添加到MessageQueue
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

//Message.java

public final class Message implements Parcelable {
  //设置Message是否为异步消息
  public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }
}

  1. Hander的 构造函数,最后都会走到最后的 Handler(Callback callback, boolean async) 重载方法。因此,Handler 默认同步Handler(mAsynchronous =false)
  2. 当Message添加到MessageQueue中的时候,根据mAsynchronous 去设置Message 是否为异步消息
//MessageQueue.java
public final class MessageQueue {

//添加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) {
        //如果MessageQueue所在线程死亡,添加的message直接回收掉
            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;
            //1.当加入的Message处理时间,小于表头Message的时间或者,MessageQueue中的Message数量为0 或者马上要处理,将新的Message 加入到表头
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                //如果当前Looper阻塞,则需要唤醒
                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.
                //当Looper阻塞,表头是一个同步屏障的时候 且,添加进来的消息是异步消息的时候,需要唤醒
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    //如果加入的message 时间小于 p 的时间
                    //则将message 插入到 p 前面的节点 
                    if (p == null || when < p.when) {
                        break;
                    }
                    //遍历如果,MessageQueue还存在异步消息的时候,不需要唤醒(neeWake=false)
                    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;
    }
}
  1. 当添加的Message插入到表头的时候,是否唤起looper 继续进行循环,取决于当前Looper是否处于阻塞
  2. 当Message 插入到 链表中的时候,当且仅当
    1.当前looper处于阻塞
    2.表头是同步屏障(handler==null)
    3.加入的消息是异步消息,且链表中在加入前不存在 异步消息
    才需要唤醒Looper进行循环。
同步屏障的发送
//MessageQueue.java
public final class MessageQueue {
public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
        //同步屏障的令牌
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
            //循环遍历,根据时间,找到 要插入的位置 :prev->message->p
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            //当MessageQueue 中没有Message时,插入表头
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
            //当MessageQueue 中不为空的时候,根据时间插入表中
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
    }

postSyncBarrier: 直接通过obtain 获得的Message,因此该Message 并没有持有 Hander

在这里插入图片描述

同步消息异步消息的处理

//MessageQueue.java
public final class MessageQueue {
 Message next() {
       
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
 
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
 
            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.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
               //普通我们发的消息为异步消息,只会走到这个方法(target 不会为null,Asynchronous且为 false)
               // mMessage  赋给 msg 返回, mMessage 并指向 mMessage 的下一个message ,    
                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;
                }
        }
}

如果,MessageQueue中,遇到了 同步屏障,则在取消息的时候。直接会获取屏障后面的异步消息,而不会取同步消息。因此,当界面绘制的时候,发送屏障 +异步消息的时候,永远都能保证这种消息,优先处理。保证了UI线程的流畅度。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值