Android 底層渲染 - 屏幕刷新機制源碼分析

相關文章鏈接:

1. Android Framework - 學習啓動篇
2. 源碼閱讀分析 - Window底層原理與系統架構

相關源碼文件:

/frameworks/base/core/java/android/view/ViewRootImpl.java
/frameworks/base/core/java/android/view/Choreographer.java
/frameworks/base/core/java/android/view/DisplayEventReceiver.java

/frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
/frameworks/native/libs/gui/DisplayEventReceiver.cpp
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
/frameworks/native/services/surfaceflinger/EventThread.cpp
/frameworks/native/libs/gui/BitTube.cpp

1. 梳理概述

在開始閱讀文章前,希望大家能認真思考幾個問題:

  • 界面卡頓的原理是怎樣的?
  • ViewRootImpl 與 SurfaceFlinger 是怎麼通信的?
  • invalidate / requestLayout 會不會立馬刷新屏幕?
  • SurfaceView / GLSurfaceView 的底層實現原理?

搞 Android 搞了幾年,我們對 VSync 信號應該會有一些瞭解,但是未必真正能理解其具體原理。比如 VSync 信號是從哪裏來的?發到哪裏去?有什麼作用?本文主要講解發到哪裏去,至於從哪裏來的大家可以看看之前的內容。

2. 請求 VSync 信號

如果我們的界面需要發生變化,一般都會來到 ViewRootImpl 的 requestLayout 方法,有可能是手動觸發的也有可能是被動觸發的,在這個方法裏面我們會主動去請求接收 VSync 信號,當下一次 VSync 信號的來的時候會主動回掉回來,然後纔開始真正的繪製流程。

    @Override
    public void requestLayout() {
      if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
      }
    }

    void scheduleTraversals() {
      if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 插入一條消息屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // post 一個 Callback
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
      }
    }

    private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
      synchronized (mLock) {
        ...
        if (dueTime <= now) {
          scheduleFrameLocked(now);
        } else {
          ...
        }
      }
    }

    private void scheduleFrameLocked(long now) {
      if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {
          // 是否在 Choreographer 的工作線程
          if (isRunningOnLooperThreadLocked()) {
            scheduleVsyncLocked();
          } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtFrontOfQueue(msg);
          }
        } else {
          ...
        }
      }
    }
    // 請求接收下一次 VSync 信號
    private void scheduleVsyncLocked() {
      mDisplayEventReceiver.scheduleVsync();
    }

由上面的源碼可以看出,每一次調用 requestLayout 方法,都會主動調用 scheduleVsync 方法來接收下一次的 VSync 信號。也就是說在下一次 VSync 信號來之前,就算連續調用 n 次的 requestLayout 方法,也並不會觸發刷新繪製流程。

3. 接收 VSync 信號

應用 App 請求了要接收下一次的 VSync 信號,那麼 SurfaceFlinger 服務怎麼把 VSync 信號,發給我們的應用 App ?這個得從 DisplayEventReceiver 的初始化入手,涉及到跨進程通信也涉及到 Native 層源碼。

    public DisplayEventReceiver(Looper looper) {
      ...
      // nativeInit 
      mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue);
    }

    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj) {
      sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, receiverWeak, messageQueue);
      // 初始化方法
      status_t status = receiver->initialize();
      receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
      return reinterpret_cast<jlong>(receiver.get());
    }

    status_t NativeDisplayEventReceiver::initialize() {
      // 接收端的 fd 添加到 Looper
      int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,this, NULL);
      return OK;
    }
    // 跨進程創建一個 mEventConnection 對象
    DisplayEventReceiver::DisplayEventReceiver() {
      sp<ISurfaceComposer> sf(ComposerService::getComposerService());
      if (sf != NULL) {
        mEventConnection = sf->createDisplayEventConnection();
        if (mEventConnection != NULL) {
            mDataChannel = mEventConnection->getDataChannel();
        }
      }
    }
    // 獲取接收端的 fd
    int DisplayEventReceiver::getFd() const {
      if (mDataChannel == NULL)
          return NO_INIT;
      return mDataChannel->getFd();
    }
    // VSync 信號來會回調到這個方法
    int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
      // Drain all pending events, keep the last vsync.
      nsecs_t vsyncTimestamp;
      int32_t vsyncDisplayId;
      uint32_t vsyncCount;
      if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        mWaitingForVsync = false;
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
      }
      return 1; // keep the callback
    }

    void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
      JNIEnv* env = AndroidRuntime::getJNIEnv();
      ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
      if (receiverObj.get()) {
        // 回掉到 Java 層的 dispatchVsync 方法
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
      }
    }

    @Override
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
      // 發消息執行 doFrame 方法,真正開始刷新繪製流程
      mTimestampNanos = timestampNanos;
      mFrame = frame;
      Message msg = Message.obtain(mHandler, this);
      msg.setAsynchronous(true);
      mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }

DisplayEventReceiver 在初始化時會創建與 SurfaceFlinger 的 Connection 連接,當應用 App 主動發起 requestNextVsync 後,SurfaceFlinger 會在下一個 VSync 信號來的時候,主動通知我們的應用 App ,回掉到 Java 層的 onVsync 方法,開始真正的刷新繪製流程。

視頻地址:https://pan.baidu.com/s/1tQ7omRNg8BgldnkjdlBPlw 
視頻密碼:6hlc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章