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

相關(guān)文章鏈接:

1. Android Framework - 學(xué)習(xí)啟動(dòng)篇
2. 源碼閱讀分析 - Window底層原理與系統(tǒng)架構(gòu)

相關(guān)源碼文件:

/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. 梳理概述

在開始閱讀文章前妈踊,希望大家能認(rèn)真思考幾個(gè)問題:

  • 界面卡頓的原理是怎樣的?
  • ViewRootImpl 與 SurfaceFlinger 是怎么通信的?
  • invalidate / requestLayout 會(huì)不會(huì)立馬刷新屏幕?
  • SurfaceView / GLSurfaceView 的底層實(shí)現(xiàn)原理?

搞 Android 搞了幾年她渴,我們對 VSync 信號(hào)應(yīng)該會(huì)有一些了解,但是未必真正能理解其具體原理。比如 VSync 信號(hào)是從哪里來的癣防?發(fā)到哪里去?有什么作用掌眠?本文主要講解發(fā)到哪里去蕾盯,至于從哪里來的大家可以看看之前的內(nèi)容。

2. 請求 VSync 信號(hào)

如果我們的界面需要發(fā)生變化蓝丙,一般都會(huì)來到 ViewRootImpl 的 requestLayout 方法级遭,有可能是手動(dòng)觸發(fā)的也有可能是被動(dòng)觸發(fā)的,在這個(gè)方法里面我們會(huì)主動(dòng)去請求接收 VSync 信號(hào)渺尘,當(dāng)下一次 VSync 信號(hào)的來的時(shí)候會(huì)主動(dòng)回掉回來挫鸽,然后才開始真正的繪制流程。

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

    void scheduleTraversals() {
      if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 插入一條消息屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // post 一個(gè) 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 信號(hào)
    private void scheduleVsyncLocked() {
      mDisplayEventReceiver.scheduleVsync();
    }

由上面的源碼可以看出鸥跟,每一次調(diào)用 requestLayout 方法丢郊,都會(huì)主動(dòng)調(diào)用 scheduleVsync 方法來接收下一次的 VSync 信號(hào)。也就是說在下一次 VSync 信號(hào)來之前锌雀,就算連續(xù)調(diào)用 n 次的 requestLayout 方法蚂夕,也并不會(huì)觸發(fā)刷新繪制流程。

3. 接收 VSync 信號(hào)

應(yīng)用 App 請求了要接收下一次的 VSync 信號(hào)腋逆,那么 SurfaceFlinger 服務(wù)怎么把 VSync 信號(hào)婿牍,發(fā)給我們的應(yīng)用 App ?這個(gè)得從 DisplayEventReceiver 的初始化入手惩歉,涉及到跨進(jìn)程通信也涉及到 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;
    }
    // 跨進(jìn)程創(chuàng)建一個(gè) 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 信號(hào)來會(huì)回調(diào)到這個(gè)方法
    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) {
      // 發(fā)消息執(zhí)行 doFrame 方法,真正開始刷新繪制流程
      mTimestampNanos = timestampNanos;
      mFrame = frame;
      Message msg = Message.obtain(mHandler, this);
      msg.setAsynchronous(true);
      mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }

DisplayEventReceiver 在初始化時(shí)會(huì)創(chuàng)建與 SurfaceFlinger 的 Connection 連接撑蚌,當(dāng)應(yīng)用 App 主動(dòng)發(fā)起 requestNextVsync 后上遥,SurfaceFlinger 會(huì)在下一個(gè) VSync 信號(hào)來的時(shí)候,主動(dòng)通知我們的應(yīng)用 App 争涌,回掉到 Java 層的 onVsync 方法粉楚,開始真正的刷新繪制流程。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市模软,隨后出現(xiàn)的幾起案子伟骨,更是在濱河造成了極大的恐慌,老刑警劉巖燃异,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件携狭,死亡現(xiàn)場離奇詭異,居然都是意外死亡回俐,警方通過查閱死者的電腦和手機(jī)逛腿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仅颇,“玉大人单默,你說我怎么就攤上這事×榱” “怎么了雕凹?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長政冻。 經(jīng)常有香客問我枚抵,道長,這世上最難降的妖魔是什么明场? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任汽摹,我火速辦了婚禮,結(jié)果婚禮上苦锨,老公的妹妹穿的比我還像新娘逼泣。我一直安慰自己,他們只是感情好舟舒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布拉庶。 她就那樣靜靜地躺著,像睡著了一般秃励。 火紅的嫁衣襯著肌膚如雪氏仗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天夺鲜,我揣著相機(jī)與錄音皆尔,去河邊找鬼。 笑死币励,一個(gè)胖子當(dāng)著我的面吹牛慷蠕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播食呻,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼流炕,長吁一口氣:“原來是場噩夢啊……” “哼澎现!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起每辟,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤昔头,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后影兽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡莱革,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年峻堰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盅视。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捐名,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闹击,到底是詐尸還是另有隱情镶蹋,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布赏半,位于F島的核電站贺归,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏断箫。R本人自食惡果不足惜拂酣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仲义。 院中可真熱鬧婶熬,春花似錦、人聲如沸埃撵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暂刘。三九已至饺谬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸳惯,已是汗流浹背商蕴。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芝发,地道東北人绪商。 一個(gè)月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像辅鲸,于是被迫代替她去往敵國和親格郁。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容