Android同步屏障机制

API:28java

​ 在阅读Android系统源码时发现,任何可能会引起View发生变化的操做,都会触发执行ViewRootImpl中的scheduleTraversals()方法,来安排下一次屏幕刷新信号到来的时候,对View树的遍历。android

scheduleTraversals()

ViewRootImpl位于android.view包下,scheduleTraversals()源码以下:数组

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 往主线程的消息队列中发送一个同步屏障
        // mTraversalBarrier是ViewRootImpl中的成员变量,用于移除同步屏障时使用
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 确保mTraversalRunnable第一时间获得执行。这里的token为null,后面回调会用到
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
复制代码

​ 咱们发现,在系统准备注册下一次屏幕刷新信号以前,往主线程的消息队列中发送了一个同步屏障消息。markdown

这么作的目的是什么呢?带着这样的疑问,咱们首先来看一下postSyncBarrier()的源码。app

postSyncBarrier()源码分析

MessageQueue位于android.os包下,postSyncBarrier()源码以下:less

/** 翻译官方注释: 直到在消息队列中遇到同步屏障时,全部的消息将正常处理。当遇到同步屏障时,同步屏障以后的同步消息将被暂停,得不到执行,直到调用了removeSyncBarrier(token)释放掉同步屏障之 后,同步消息将继续执行。 此方法用于当即延迟全部后续发布的同步消息的执行,直到知足释放同步屏障的条件为止。异步消息不受屏障的影响,并继续照常进行处理。此调用必须始终以相同的token来调用removeSyncBarrier(token),以确保消息队列恢复正常运行,不然应用程序可能会挂起! */
// 这是一个标记为@hide的隐藏方法,也就是说系统不容许咱们调用,只容许本身用
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) {
        // mNextBarrierToken是MessageQueue中的成员变量,从0开始递增
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse(); //同步屏障消息建立出来以后,直接标识为使用中
        msg.when = when;
        msg.arg1 = token;

        /* 咱们发现,这个特殊的同步屏障消息msg.target没有赋值,是为null的;正常的消息都是有target的。也就是说不须要对应的Handler来处理这个消息, 只是起到了一个标记的做用*/

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            // 该循环执行完以后,就找到了同步屏障插入的消息队列的位置
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }

        if (prev != null) { // invariant: p == prev.next
            // 消息队列不为空,插入同步屏障消息到消息队列中
            msg.next = p;
            prev.next = msg;
        } else {
            // 消息队列为空,消息队列指针指向同步屏障
            msg.next = p;
            mMessages = msg;
        }
        // 返回token,用以调用removeSyncBarrier(token)来释放同步屏障
        return token;
    }
}
复制代码

​ 经过上面对源码的注释,咱们知道,postSyncBarrier()方法的做用就是,往消息队列中添加了一个特殊的Message消息,来实现同步屏障的效果。异步

同步屏障的做用就是为了确保:同步屏障以后的全部同步消息都将被暂停,得不到执行,直到调用了removeSyncBarrier(token)释放掉同步屏障,全部的同步消息将继续执行。也就是说,同步屏障以后的异步消息将会优先获得执行!async

Choreographer.postCallback()源码分析

​ 回到ViewRootImpl中,在发送完同步屏障以后,系统经过Choreographer调用postCallback()发送了一个mTraversalRunnable,确保mTraversalRunnable能够第一时间获得执行。ide

​ 在ChoreographerpostCallback()方法最终实现是在postCallbackDelayedInternal()方法内部,咱们来简单看一下postCallbackDelayedInternal()方法内部的实现逻辑。oop

Choreographer位于android.view包下,postCallbackDelayedInternal()源码以下:

private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }

    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        // 把咱们的mTraversalRunnable记录到CallbackQueue回调队列中,token为null
        // CallbackQueue回调队列按回调类型分类
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            // 由于postCallback()调用时delayMillis为0,因此dueTime==now
            scheduleFrameLocked(now);
        } else {
            // 若是是一个延时消息,则往主线程消息队列发送一个异步消息,最终仍是会执行到
            // scheduleFrameLocked()方法
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}
复制代码

​ 接下来咱们再看一下scheduleFrameLocked()方法的具体实现。

private void scheduleFrameLocked(long now) {
    // 若是已经注册了下一次屏幕刷新信号则跳过,避免重复注册
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        // USE_VSYNC是一个静态内部变量,默认状况下都为true
        // 按字面意思来理解,应该是使用垂直同步的意思
        if (USE_VSYNC) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "Scheduling next frame on vsync.");
            }

            // If running on the Looper thread, then schedule the vsync immediately,
            // otherwise post a message to schedule the vsync from the UI thread
            // as soon as possible.
            // 若是是在UI线程,当即安排注册下一次垂直同步信号
            // 若是不是在UI线程,往主线程添加一个异步消息,以尽快安排注册下一次垂直同步信号
            if (isRunningOnLooperThreadLocked()) {
                // 最终会调用DisplayEventReceiver中的nativeScheduleVsync()注册下一次垂直同步信号
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            final long nextFrameTime = Math.max(
                    mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
            if (DEBUG_FRAMES) {
                Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
            }
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        }
    }
}
复制代码

​ 经过上面对源码的注释,咱们发现最终会调用到DisplayEventReceiver中的nativeScheduleVsync()方法,来注册下一次垂直同步信号的到来。

FrameDisplayEventReceiver.onVsync()源码分析

​ 在Choreographer中,当下一次垂直同步信号到来时,会回调FrameDisplayEventReceiver中的onVsync()方法,接下来,咱们来看一下onVsync()方法的源码实现。

FrameDisplayEventReceiverChoreographer中的私有成员内部类,onVsync()具体实现以下:

// 当接收到垂直同步信号时调用该方法
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
    // Ignore vsync from secondary display.
    // This can be problematic because the call to scheduleVsync() is a one-shot.
    // We need to ensure that we will still receive the vsync from the primary
    // display which is the one we really care about. Ideally we should schedule
    // vsync for a particular display.
    // At this time Surface Flinger won't send us vsyncs for secondary displays
    // but that could change in the future so let's log a message to help us remember
    // that we need to fix this.
    if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
        Log.d(TAG, "Received vsync from secondary display, but we don't support "
                + "this case yet. Choreographer needs a way to explicitly request "
                + "vsync for a specific display to ensure it doesn't lose track "
                + "of its scheduled vsync.");
        scheduleVsync();
        return;
    }

    // Post the vsync event to the Handler.
    // The idea is to prevent incoming vsync events from completely starving
    // the message queue. If there are no messages in the queue with timestamps
    // earlier than the frame time, then the vsync event will be processed immediately.
    // Otherwise, messages that predate the vsync event will be handled first.
    long now = System.nanoTime();
    if (timestampNanos > now) {
        Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                + " ms in the future! Check that graphics HAL is generating vsync "
                + "timestamps using the correct timebase.");
        timestampNanos = now;
    }

    if (mHavePendingVsync) {
        Log.w(TAG, "Already have a pending vsync event. There should only be "
                + "one at a time.");
    } else {
        // 修改标记值,表示有一个立刻要执行的垂直同步信号
        // 该标记值只是标记做用,并无实际参与逻辑,只是打印了上面的警告日志
        mHavePendingVsync = true;
    }

    // 当接收到垂直同步信号以后,往主线程发送了一个异步消息;该异步消息的callback为
    // FrameDisplayEventReceiver自己,由于FrameDisplayEventReceiver实现了Runnable接口
    mTimestampNanos = timestampNanos;
    mFrame = frame;
    Message msg = Message.obtain(mHandler, this);
    msg.setAsynchronous(true);
    mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
复制代码

​ 经过上面对源码的注释信息,咱们发现,当垂直同步信号到来时,Android系统往主线程消息队列中发送了一个异步消息。而这个异步消息,也就是咱们前面发送同步屏障的做用,来确保这个异步消息能够第一时间执行。

​ 咱们知道,目前全部的显示设备都有一个固定的刷新率,好比如今Android手机上常见的60HZ90HZ120HZ,简单来讲,就是刷新率为多少HZ,就表示每秒钟刷新多少次,60HZ就是每秒钟刷新60次。为了确保整个显示效果的流畅顺滑,Android系统在每一次垂直信号到来时,尽量都会在第一时间进行处理,来绘制界面的内容。咱们View的onMeasure()onLayout()onDraw()三大流程,都是发生在这个过程当中。因此,这三个方法若是比较耗时,超过了每帧平均耗时(每帧平均耗时=1000ms / 屏幕刷新率 ),则给用户的体验就是咱们的应用比较卡顿,体验较差,也是咱们着重优化的方向!

​ 接下来,咱们来看一下FrameDisplayEventReceiverrun()方法的具体实现逻辑。

@Override
public void run() {
    // 重置标记值为false
    mHavePendingVsync = false;
    // 执行当前帧逻辑
    doFrame(mTimestampNanos, mFrame);
}
复制代码

doFrame()源码分析

doFrame()方法位于Choreographer中,源码以下:

void doFrame(long frameTimeNanos, int frame) {
  final long startNanos;
  synchronized (mLock) {
      if (!mFrameScheduled) {
          return; // no work to do
      }

      if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
          mDebugPrintNextFrameTimeDelta = false;
          Log.d(TAG, "Frame time delta: "
                  + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
      }

      long intendedFrameTimeNanos = frameTimeNanos;
      startNanos = System.nanoTime();
      final long jitterNanos = startNanos - frameTimeNanos;
      if (jitterNanos >= mFrameIntervalNanos) {
          // 丢帧啦...
          final long skippedFrames = jitterNanos / mFrameIntervalNanos;
          if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
              Log.i(TAG, "Skipped " + skippedFrames + " frames! "
                      + "The application may be doing too much work on its main thread.");
          }
          final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
          if (DEBUG_JANK) {
              Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                      + "which is more than the frame interval of "
                      + (mFrameIntervalNanos * 0.000001f) + " ms! "
                      + "Skipping " + skippedFrames + " frames and setting frame "
                      + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
          }
          frameTimeNanos = startNanos - lastFrameOffset;
      }

      if (frameTimeNanos < mLastFrameTimeNanos) {
          if (DEBUG_JANK) {
              Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
                      + "previously skipped frame. Waiting for next vsync.");
          }
          scheduleVsyncLocked();
          return;
      }

      if (mFPSDivisor > 1) {
          long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
          if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
              scheduleVsyncLocked();
              return;
          }
      }

      mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
      mFrameScheduled = false;
      mLastFrameTimeNanos = frameTimeNanos;
  }

  try {
      Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
      AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

      // CALLBACK_INPUT事件首先回调
      mFrameInfo.markInputHandlingStart();
      doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

      // 接下来回调CALLBACK_ANIMATION事件
      mFrameInfo.markAnimationsStart();
      doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

      // 再接下来回调CALLBACK_TRAVERSAL事件
      mFrameInfo.markPerformTraversalsStart();
      doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

      doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
  } finally {
      AnimationUtils.unlockAnimationClock();
      Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  }

  if (DEBUG_FRAMES) {
      final long endNanos = System.nanoTime();
      Log.d(TAG, "Frame " + frame + ": Finished, took "
              + (endNanos - startNanos) * 0.000001f + " ms, latency "
              + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
  }
}
复制代码

​ 咱们发现,doCallbacks()的回调是有前后顺序的。想一想看也是合理的!CALLBACK_INPUT的卡顿应该是最明显的,咱们首先进行处理,CALLBACK_ANIMATION次之,最后是CALLBACK_TRAVERSAL。

​ 咱们再来看一下doCallbacks()的源码实现:

void doCallbacks(int callbackType, long frameTimeNanos) {
    CallbackRecord callbacks;
    synchronized (mLock) {
        // We use "now" to determine when callbacks become due because it's possible
        // for earlier processing phases in a frame to post callbacks that should run
        // in a following phase, such as an input event that causes an animation to start.
        final long now = System.nanoTime();
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                now / TimeUtils.NANOS_PER_MS);
        if (callbacks == null) {
            return;
        }
        // 标记当前正在执行callback回调
        mCallbacksRunning = true;

        // Update the frame time if necessary when committing the frame.
        // We only update the frame time if we are more than 2 frames late reaching
        // the commit phase. This ensures that the frame time which is observed by the
        // callbacks will always increase from one frame to the next and never repeat.
        // We never want the next frame's starting frame time to end up being less than
        // or equal to the previous frame's commit frame time. Keep in mind that the
        // next frame has most likely already been scheduled by now so we play it
        // safe by ensuring the commit time is always at least one frame behind.
        if (callbackType == Choreographer.CALLBACK_COMMIT) {
            final long jitterNanos = now - frameTimeNanos;
            Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
            if (jitterNanos >= 2 * mFrameIntervalNanos) {
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                        + mFrameIntervalNanos;
                if (DEBUG_JANK) {
                    Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                            + " ms which is more than twice the frame interval of "
                            + (mFrameIntervalNanos * 0.000001f) + " ms! "
                            + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                            + " ms in the past.");
                    mDebugPrintNextFrameTimeDelta = true;
                }
                frameTimeNanos = now - lastFrameOffset;
                mLastFrameTimeNanos = frameTimeNanos;
            }
        }
    }
    try {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "RunCallback: type=" + callbackType
                        + ", action=" + c.action + ", token=" + c.token
                        + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
            }

            // 执行回调方法
            c.run(frameTimeNanos);
        }
    } finally {
        synchronized (mLock) {
            // 重置callback回调标记值为false
            mCallbacksRunning = false;
            do {
                final CallbackRecord next = callbacks.next;
                recycleCallbackLocked(callbacks);
                callbacks = next;
            } while (callbacks != null);
        }
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}
复制代码

CallbackRecord源码分析

CallbackRecordChoreographer中的私有静态内部类,CallbackRecord中的run()方法的具体实现:

private static final class CallbackRecord {
    public CallbackRecord next;
    public long dueTime;
    public Object action; // Runnable or FrameCallback
    public Object token;

    public void run(long frameTimeNanos) {
        if (token == FRAME_CALLBACK_TOKEN) {
            ((FrameCallback)action).doFrame(frameTimeNanos);
        } else {
            // 经过前面的分析,咱们前面经过postCallback()发送的回调token为null,因此会执行ViewRootImpl中mTraversalRunnable的run()方法
            // 咱们如今终于明白,前面发送同步屏障,目的就是为了确保mTraversalRunnable的run()方法可以第一时间获得执行
            ((Runnable)action).run();
        }
    }
}
复制代码

​ 到这里,咱们终于明白,咱们在前面经过ViewRootImpl类中的scheduleTraversals()方法,发送的同步屏障消息,是为了确保mTraversalRunnable可以第一时间获得执行。其中,mTraversalRunnableViewRootImpl中成员变量,具体实现为TraversalRunnableTraversalRunnable则为ViewRootImpl中成员内部类,具体实现以下:

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        // 在这里,真正开始执行View树遍历
        doTraversal();
    }
}
复制代码

doTraversal()

​ 搞清楚了同步屏障的做用以后,咱们接下来分析一下ViewRootImpl中真正开始执行View树遍历的地方,也就是在这里移除了上面添加的同步屏障,也就是doTraversal()方法的实现。

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // 移除同步屏障消息,消息队列得以正常进行
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        // 开始真正执行View树的遍历
        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}
复制代码

removeSyncBarrier()源码分析

​ 那么,这个移除同步屏障的内部逻辑又是怎么实现的呢?咱们来分析一下removeSyncBarrier()的源码。

// 这里一样也标记为@hide,和postSyncBarrier()是一对儿,系统也不容许咱们调用
public void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        // 这里循环执行完毕,就找到了对用token的同步屏障消息
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }

        // 若是这里为null,说明这个token对应的同步屏障消息要么尚未添加到消息队列中;要么已经被移除掉了,抛出异常!
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            // 从消息队列中移除同步屏障消息;当前消息循环已经在运行中,不须要再次唤醒
            prev.next = p.next;
            needWake = false;
        } else {
            // 同步屏障消息位于消息队列第一个,从消息队列中移除同步屏障。
            // 当前消息循环为阻塞状态;若是下一个消息为null,或者下一个消息的target不为null,则唤醒消息循环
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        // 回收Message消息,循环利用
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        // 若是当前消息循环正在退出,则说明它已经唤醒了
        // 咱们认为当mQuitting为false时,mPtr确定不为0,也就是正常状态
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}
复制代码

MessageQueue.next()

​ 咱们来看一下MessageQueue中获取消息时,遇到同步屏障时的处理逻辑。也就是next()方法的源码:

// API版本28
// android.os.MessageQueue.java

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) { //当消息循环已经退出,则直接返回null
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        //该处调用是Looper线程休眠的核心逻辑;当等待了nextPollTimeoutMillis时长以后,或者消息队列被唤醒,都会从该方法处返回
        //当nextPollTimeoutMillis值为-1时,表示当前消息队列中没有要处理的消息,则会一直休眠下去,直到被唤醒
        //当nextPollTimeoutMillis值为0时,最终会执行到native层的epoll_wait()方法,该方法会当即返回,不会进入休眠
        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.
                
                // 特殊消息类型,表示消息队列中有同步屏障存在;此逻辑会找到同步屏障后第一个异步消息
                // 若是没找到异步消息时,则会把nextPollTimeoutMillis赋值为-1,在下次轮询时,消息队列将进入阻塞
                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; //成功获取到MessageQueue中的下一条将要执行的消息
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1; // 没有消息,进入无线休眠,直到被唤醒
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) { //MessageQueue正在退出,则返回null
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            
            // 能执行到这里,说明消息队列在下一次轮询中立刻就要进入休眠状态啦
            // 当消息队列为空,或者还没到下一次消息的执行时间时,给pendingIdleHandlerCount从新赋值,准备执行IdleHandler中相关逻辑
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }

            // 若是没有IdleHandler须要执行,则标记当前消息队列为阻塞状态,进入下一次轮询,下一次轮询时会阻塞
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run. Loop and wait some more.
                mBlocked = true;
                continue;
            }

            // 初始化mPendingIdleHandlers
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            // 给即将要运行的IdleHandler数组任务赋值,经过mIdleHandlers转换为IdleHandler数组
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        // 当前消息队列处于空闲状态,执行IdleHandler逻辑
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // 获取完以后,释放数组中对IdleHandler的引用

            boolean keep = false;
            try {
            	// 执行IdleHandler空闲逻辑,该方法须要返回一个boolean值
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            // 当IdleHandler不须要保存时,keep为false,执行完一次后,从mIdleHandlers移除该任务
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0; //重置pendingIdleHandlerCount为0,以保证该次获取消息时不会重复执行

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        
        // 当IdleHandler执行完时,可能已经到了下一个消息执行的时间,所以,在下一次轮询时无需等待,直接获取下一个消息
        // 当nextPollTimeoutMillis为0时,nativePollOnce()方法底层逻辑会当即返回,不会阻塞休眠
        nextPollTimeoutMillis = 0; 
    }
}
复制代码

总结

​ 咱们对上面的分析流程作一个总结:

  • 任何可能会引起View发生变化的操做,都会触发执行ViewRootImpl中的scheduleTraversals()方法,来安排下一次屏幕刷新信号到来的时候,对View树的遍历。

  • scheduleTraversals()方法内部,会首先往主线程的消息队列中发送一个同步屏障,这个同步屏障其实就是一个特殊的Message(),这个特殊的msg.target没有赋值,是为null的;而正常的消息都是有target的。以此来起到一个同步屏障的目的:确保同步屏障以后的异步消息能优先获得执行!其实,最终是为了确保doTraversal()优先获得执行,去真正地开始执行View树遍历。

  • doTraversal()方法中,移除了前面scheduleTraversals()方法中发送的同步屏障,确保消息队列得以正常继续运行;接下来调用performTraversals()真正地开始执行View树遍历。

相关文章
相关标签/搜索