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樹遍歷。

相關文章
相關標籤/搜索