Handler 二期

您所在的位置:网站首页 handler内存屏障 Handler 二期

Handler 二期

2023-08-24 21:45| 来源: 网络整理| 查看: 265

之前说过一次Handler,主要介绍了sendMessage 到 handleMessage的流程,具体的细节并没有过多涉及,本文会补充Handler的两个细节:

阻塞唤醒机制 内存屏障

Looper

Looper的创建: Looper构造方法: private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

Looper只有一个私有构造方法,在构造方法中,会创建一个MessageQueue对象;

主线程Looper对象的创建: public static void prepareMainLooper() { // 分析1 prepare(false); synchronized (Looper.class) { // 分析2 if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } // 分析3 sMainLooper = myLooper(); } } // 分析1 private static void prepare(boolean quitAllowed) { // 分析2 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } // 分析3 public static @Nullable Looper myLooper() { return sThreadLocal.get(); }

在ActivityThread的main中调用prepareMainLooper()为当前App的主线程构造一个Looper对象;

分析1: 调用prepare() new一个Looper

分析: 保证一个线程只能创建一个Looper对象

分析3:通过ThreadLocal做到Looper的线程隔离

子线程Looper的创建就是调用prepare(),注意这里的quitAllowed参数,表示Looper是否可以quit(),子线程可以,主线程不可以; Loop的循环:loop() public static void loop() { // 获取当前Looper的MessageQueue对象 final MessageQueue queue = me.mQueue; // 死循环,不断的从MessageQueue中取消息 for (;;) { // 分析1 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } try { // 分析2 msg.target.dispatchMessage(msg); } } }

在ActivityThread的main中构造了主线的Looper对象后,会调用Looper的loop(),在loop()首先获取当前Looper绑定的MessageQueue对象,然后在一个死循环里不断的取Message,分发给对应的Handler处理

分析1: 调用MessageQueue的next()获取一个Message对象,这个方法是一个阻塞方法,后面会详细讲; 分析2:分发给对应的handler处理;上篇文章有详细介绍; Looper循环的退出:quit() public void quit() { mQueue.quit(false); }

Looper的退出调用了MessageQueue的退出,具体内容在MessageQueue部分展开;

MessageQueue

在具体介绍MessageQueue之前,我们先看一下MessageQueue是如何保存Message的:

public final class MessageQueue { Message mMessages; } public final class Message implements Parcelable { /*package*/ Message next; }

我们可以看到MessageQueue的内部只有一个Message成员,并没有其他的数据结构存放Message; 而Message本身是一个链表的结点;

MessageQueue获取Message :next() Message next() { // 分析1 int nextPollTimeoutMillis = 0; for (;;) { // 分析2 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { if (msg != null) { // 分析3 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 { mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; return msg; } } else { // No more messages. // 分析4 nextPollTimeoutMillis = -1; } // 分析5 // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } } 分析1:nextPollTimeoutMillis字段,用于判断是否阻塞 分析2:nativePollOnce()这是一个native方法,用于实现next()的阻塞,根据nextPollTimeoutMillis的大小,-1表示一直阻塞等到有人唤醒,0表示不阻塞,大于0表示阻塞多少时间后自动唤醒; 分析3:MessageQueue是一个优先级队列,当取出的Message还没有没有到执行时间时,会计算出一个时间差,通过nativePollOnce()取阻塞一定时间; 分析4:如果没有取到Message,将nextPollTimeoutMillis字段置为-1,一直阻塞等待唤醒; 分析5:检查mQuitting字段,上文的Looper退出循环调用quit会将此字段设置为true,回收当前的MessageQueue; MessageQueue添加Message:enqueueMessage() boolean enqueueMessage(Message msg, long when) { synchronized (this) { // 分析1 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; // 分析:2 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; } // 分析3 // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }

MessageQueue是一个优先级队列,在使用Handler提交Message的时候,会附加一个时间,这个时间会通过when字段保存,在添加到队列的时候,会通过插入排序的方式找到当前Message在队列的合适位置;

分析1: Message的优先级when字段 分析2:插入排序 分析3:唤醒操作,当next()中的nativePollOnce()传入-1时,会一直阻塞,在这里会唤醒这个阻塞;

Message

Message的复用:obtain() private static Message sPool; // 缓存链表 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(); }

在Message类的内部会有一个Pool链表,用来复用Message对象,调用obtain()创建Message时会优先从Pool中取,如果娶不到才会去new;

Message Pool的添加: void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } } public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); }

在recycleUnchecked()中,先将Message对象的所有成员清空,如果Pool没有超过容量的话就添加到pool中;外部使用recycle()复用Message,这个方法的调用时机很多,ActivityThread的H,Wifi服务都用了这个回收

内存屏障:

在安卓中,整个App的运行都是在Loop.loop()死循环中完成了,包括我们的项目逻辑,安卓的系统逻辑,比如屏幕的刷新等,安卓系统规定了屏幕一般16ms刷新一次,刷新操作是由ViewRootImpl来实现的,触发屏幕刷新的机制也是一个Message消息,但是我们刷新屏幕的Message必须是立刻执行的,由于MessageQueue是优先级队列的原理,无法做到实时的刷新,就提出了Handler内存屏障的概念;

Message节点被分成两种:同步和异步 message.setAsynchronous(true); // 设置为异步Message

Message有一个字段,用于表示异步还是同步,默认是同步,可以通过setAsynchronous()设置,在一些特殊的场景下,比如View的刷新,必须立刻执行,就需要设置异步Message;

内存屏障的打开与关闭:

单纯的设置同步和异步是无法保证立刻执行的,还必须打开内存屏障:内存屏障的本质是一个target==null的Message,当loop的时候发现有内存屏障的时候,此时只会处理异步Message,保证了异步Message的立刻执行;

MessageQueue.postSyncBarrier() 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) { while (p != null && p.when


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3