<form id="dlljd"></form>
        <address id="dlljd"><address id="dlljd"><listing id="dlljd"></listing></address></address>

        <em id="dlljd"><form id="dlljd"></form></em>

          <address id="dlljd"></address>
            <noframes id="dlljd">

              聯系我們 - 廣告服務 - 聯系電話:
              您的當前位置: > 關注 > > 正文

              View繪制流程2-安卓如何執行measure/draw 全球最新

              來源:CSDN 時間:2023-04-24 08:29:22

              前言:

              vsync信號其實主要有兩種,APP和SF兩種。


              (資料圖)

              兩種類似的信號分別具有以下的作用:

              1.APP類型,我們常說的Vsync信號其實指的就是這種類型。

              2.SF類型,我感覺有可能是SurfaceFlinger的縮寫,這個同步信號主要是通知進行數據buffer的合成。

              本文主要探討的主APP類型的Vsync信號,SF類型的會在另外一篇文章,View繪制流程中講解。

              一.整體流程簡介

              我把app-VSync的整個流程分為四大塊:

              第一塊,APP側發出請求信號,通知到SurfaceFlinger;

              第二塊,SurfaceFlinger收到通知后,作為消費者側去緩存池中查詢是否存在VSYNC,如果有,則通知APP側。

              第三塊,SurfaceFlinger中的生產者邏輯,生產下一次的Vsync信號。

              第四塊,APP側收到Vsync信號后進行處理,最終完成繪制流程。

              整體的流程圖如下圖所示,后續文章也會按照這四大塊流程去細講。

              二.客戶端發出信號

              2.1 java端流轉

              我們把代碼的開始點設置為ViewRootImpl中的scheduleTraversals方法,如果想了解這個方法之前的流程,可以參看我的另外一篇文章:

              View繪制流程2-安卓是如何執行measure/layout/draw三個繪制流程_失落夏天的博客-CSDN博客

              scheduleTraversals是負責渲染流程的,界面上任何布局上的改動,最終都會執行這個方法進行刷新操作。scheduleTraversals會通過Choreographer的postCallback方法去請求Vsync信號并且設置回調方法。

              mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

              Choreographer中的最終實現是postCallbackDelayedInternal方法:

              private void postCallbackDelayedInternal(int callbackType,            Object action, Object token, long delayMillis) {        ...        synchronized (mLock) {            final long now = SystemClock.uptimeMillis();            final long dueTime = now + delayMillis;            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);            if (dueTime <= now) {                scheduleFrameLocked(now);            }            ...        }    }

              這個方法會把回調加入到mCallbackQueues中,然后通過scheduleFrameLocked方法開始請求Vsync信號。

              scheduleFrameLocked中又會進行層層傳遞,最終調用到native方法。傳遞關系如下:

              scheduleFrameLocked(Choreographer.java)->scheduleVsyncLocked(Choreographer.java)->scheduleVsync(DisplayEventReceiver.java)->nativeScheduleVsync(DisplayEventReceiver.java)

              2.2 native方法中的邏輯流轉

              nativeScheduleVsync在native層的注冊是DisplayEventReceiver.cpp中的nativeScheduleVsync方法,方法中通過NativeDisplayEventReceiver的scheduleVsync來完成請求:

              static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {    spreceiver =            reinterpret_cast(receiverPtr);    status_t status = receiver->scheduleVsync();   ...}

              NativeDisplayEventReceiver是DisplayEventDispatcher的子類,scheduleVsync方法會執行到DisplayEventDispatcher中的scheduleVsync方法,調用DisplayEventReceiver的requestNextVsync繼續請求流程。

              status_t DisplayEventDispatcher::scheduleVsync() {    if (!mWaitingForVsync) {        ...        status_t status = mReceiver.requestNextVsync();        ...        mWaitingForVsync = true;        ...    }    return OK;}

              這里有一個本地變量mWaitingForVsync,如果請求了一次sync,就會改為true,接下來的Vsync請求,都不會傳輸到SurfaceFlinger一層了,避免重復無意義請求。只有等到收到Vsync信號的時候,才會改為false。

              DisplayEventReceiver中,會交給mEventConnection處理:

              status_t DisplayEventReceiver::requestNextVsync() {    if (mEventConnection != nullptr) {        mEventConnection->requestNextVsync();        return NO_ERROR;    }    return NO_INIT;}

              mEventConnection其實是一個binder客戶端,是在創建DisplayEventReceiver的時候通過binder方法創建的,其binder服務端實現在SurfaceFlinger進程側的EventThread.cpp。

              DisplayEventReceiver::DisplayEventReceiver(        ISurfaceComposer::VsyncSource vsyncSource,        ISurfaceComposer::EventRegistrationFlags eventRegistration) {    spsf(ComposerService::getComposerService());    if (sf != nullptr) {        mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);        ...    }}

              三.SurfaceFlinger端Vsync信號消費者

              首先,SurfaceFlinger的總體結構圖如下,SurfaceFlinger中存在一個Scheduler,Scheduler中存在多個VsyncDispatch。其中VSYNC-app負責所有APP的VSYNC信號的處理,本文主要講的也是這個流程中的。

              其次,下圖是主流程圖中的一部分,也是本章要講的內容。

              3.1 EventThread收到通知后,轉發EventThread的工作線程

              是接收方法如下,轉發到mEventThread的requestNextVsync方法中。

              binder::Status EventThreadConnection::requestNextVsync() {    ATRACE_CALL();    mEventThread->requestNextVsync(this);    return binder::Status::ok();}

              我們接著看一下EventThread中的requestNextVsync方法:

              void EventThread::requestNextVsync(const sp& connection) {    ...    if (connection->vsyncRequest == VSyncRequest::None) {        connection->vsyncRequest = VSyncRequest::Single;        mCondition.notify_all();    } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {        connection->vsyncRequest = VSyncRequest::Single;    }}

              這里的邏輯很簡單,如果當前的VSYNC狀態是None的話,釋放鎖mCondition。

              這里既然notify_all,那么一定有地方wait等待鎖,而等待的地方就是threadMain方法。

              3.2 threadMain方法

              這個方法算是整個Vsync流程的核心,它是一個無限循環的模式,其作用是不斷的從mPendingEvents中獲取Vsync信號,然后轉交給APP端。并且在一次Vsync流程結束后,又通過VsyncSource請求下一次的Vsync信號。

              threadMain方法是EventThread類初始化的時候創建的線程去執行的方法:

              mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {        std::unique_locklock(mMutex);        threadMain(lock);    });

              我們看一下精簡后的threadMain方法:

              void EventThread::threadMain(std::unique_lock& lock) {    DisplayEventConsumers consumers;    //只要沒有退出,就一直循環    while (mState != State::Quit) {        執行以下1-7的操作           }}

              總體來說,分為以下四大步驟:

              1.作為消費者嘗試從mPendingEvents中獲取Vsync信號,如果獲取成功,則賦值給event。

              std::optionalevent;        //查看mPendingEvents中是否存在Vsync信號        if (!mPendingEvents.empty()) {            event = mPendingEvents.front();            mPendingEvents.pop_front();            ...        }

              2.計算vsyncRequested的狀態,只要客戶端消費者的Connection保持連接,則vsyncRequested=true,并且上面步驟一獲取到event的話,則把消費者的connection加入到consumers集合中。

              bool vsyncRequested = false;       //獲取當前的狀態,并且判斷是否有客戶端的消費者在請求,如果有則加入到consumers集合中        auto it = mDisplayEventConnections.begin();        while (it != mDisplayEventConnections.end()) {            if (const auto connection = it->promote()) {                //客戶端還是處于請求的狀態                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;                if (event && shouldConsumeEvent(*event, connection)) {                    consumers.push_back(connection);                }                ++it;            } else {                it = mDisplayEventConnections.erase(it);            }        }

              3.如果consumers集合不為空,則進行消費。把Vsync信號分發給消費者。(具體步驟我們下一小節中講)

              //如果消費者不為空,則通過dispatchEvent方法最終通知到APP一側        if (!consumers.empty()) {            dispatchEvent(*event, consumers);            consumers.clear();        }

              4.獲取下一個狀態

              //mVSyncState不會為空,則主要是根據vsyncRequested來判斷的。vsyncRequested上面計算的        State nextState;        if (mVSyncState && vsyncRequested) {            nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;        } else {            ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");            nextState = State::Idle;        }

              5.進行判斷,確定是否需要請求VSYNC信號。這一塊挺有意思的,簡單理解如下:

              如果當前是VSYNC狀態,下一個狀態也是VSYNC狀態,那么說明信號還沒來,所以沒必要重復發送。

              如果當前是Idle狀態,下一個狀態是VSYNC狀態,那么則要進行VSYNC信號請求。

              如果當前是VSYNC狀態,下一個狀態也是Idle狀態,那么說明信號已經來了,下一次的客戶端請求還沒來,所以不要進行VSYNC信號請求,則會進行取消操作。

              if (mState != nextState) {            if (mState == State::VSync) {                mVSyncSource->setVSyncEnabled(false);            } else if (nextState == State::VSync) {                //如果下一個狀態還是VSync,則繼續去請求VSYNC信號                mVSyncSource->setVSyncEnabled(true);            }            mState = nextState;        }

              如何開始和結束去進行VSYNC信號獲取的獲取操作,我們第四章中講,這個主要就是消費者邏輯了。

              6.如果event為空,說明mPendingEvents中已經取光了,則進入休眠操作。

              反之event不為空,說明mPendingEvents中也許還存在未消費的VSYNC信號,則contine繼續消費。

              //如果處理了event,那么說明此次已經拿到了Vsync信號,說明后面有可能還有,則繼續拿        if (event) {            continue;        }

              7.進入休眠或者超時之后主動模擬信號加入到mPendingEvents中。

              //說明Vsync信號已經消費完了,則進入休眠模式,等到APP側的下一次通知進行喚醒        // Wait for event or client registration/request.        if (mState == State::Idle) {            mCondition.wait(lock);        } else {            // Generate a fake VSYNC after a long timeout in case the driver stalls. When the            // display is off, keep feeding clients at 60 Hz.            const std::chrono::nanoseconds timeout =                    mState == State::SyntheticVSync ? 16ms : 1000ms;            if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {                if (mState == State::VSync) {                    ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);                    std::string debugInfo = "VsyncSource debug info:\n";                    mVSyncSource->dump(debugInfo);                    // Log the debug info line-by-line to avoid logcat overflow                    auto pos = debugInfo.find("\n");                    while (pos != std::string::npos) {                        ALOGW("%s", debugInfo.substr(0, pos).c_str());                        debugInfo = debugInfo.substr(pos + 1);                        pos = debugInfo.find("\n");                    }                }                LOG_FATAL_IF(!mVSyncState);                const auto now = systemTime(SYSTEM_TIME_MONOTONIC);                const auto deadlineTimestamp = now + timeout.count();                const auto expectedVSyncTime = deadlineTimestamp + timeout.count();                mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,                                                   ++mVSyncState->count, expectedVSyncTime,                                                   deadlineTimestamp));            }        }

              3.3  dispatchEvent方法把Vsync信號分發給消費者

              首先遍歷消費者,調用postEvent進行通知

              void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,                                const DisplayEventConsumers& consumers) {     for (const auto& consumer : consumers) {            ...            switch (consumer->postEvent(copy)) {            }     }}

              然后postEvent方法中,調用sendEvent進行信號的發送

              status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {   ...        auto size = DisplayEventReceiver::sendEvents(&mChannel, mPendingEvents.data(),                                                     mPendingEvents.size());    ...}

              最終通過Socket的方法進行信號的發送,接受者就是APP側了。

              ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,        Event const* events, size_t count){    return gui::BitTube::sendObjects(dataChannel, events, count);}

              四.SurfaceFlinger端生產者

              上面講到通過setVSyncEnabled方法去開始或者結束獲取Vsync信號的操作。

              4.1 獲取VSYNC信號

              setVSyncEnabled方法如下:

              void DispSyncSource::setVSyncEnabled(bool enable) {    std::lock_guard lock(mVsyncMutex);    if (enable) {        mCallbackRepeater->start(mWorkDuration, mReadyDuration);    } else {        mCallbackRepeater->stop();    }    mEnabled = enable;}

              對應的其實就是mCallbackRepeater的start和stop方法,其實現類是DispSyncSource.cpp中的CallbackRepeater。

              我們這里看到一個成員變量mWorkDuration,這個值其實就是控制Vscyn觸發時間的。這個我們后續小節再講,這里只是知道有這個值就好了。

              4.2 把時間設置給Timer定時觸發

              start方法中,記錄一下傳入的workDuration時間,然后傳遞給mRegistration處理。

              void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {        std::lock_guard lock(mMutex);        mStarted = true;        mWorkDuration = workDuration;        mReadyDuration = readyDuration;        auto const scheduleResult =                mRegistration.schedule({.workDuration = mWorkDuration.count(),                                        .readyDuration = mReadyDuration.count(),                                        .earliestVsync = mLastCallTime.count()});           }

              mRegistration的實現類是VSyncCallbackRegistration,其中schedule方法也是交給VSyncDispatchTimerQueue來處理:

              ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {    if (!mValidToken) {        return std::nullopt;    }    return mDispatch.get().schedule(mToken, scheduleTiming);}

              schedule方法中,進行一系列的合法判斷,最終會交給 rearmTimerSkippingUpdateFor方法處理。

              然后我們就可以看到rearmTimerSkippingUpdateFor中去調用setTimer方法去設置定時觸發。

              rearmTimerSkippingUpdateFor方法略,

              setTimer方法如下:

              void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {    mIntendedWakeupTime = targetTime;    mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),                         mIntendedWakeupTime);    mLastTimerSchedule = mTimeKeeper->now();}

              則到了targetTime之后,就會執行timerCallBack方法。

              4.3 生成Vsync信號加入mPendingEvents

              timerCallback方法如下:

              void VSyncDispatchTimerQueue::timerCallback() {    struct Invocation {        std::shared_ptrcallback;        nsecs_t vsyncTimestamp;        nsecs_t wakeupTimestamp;        nsecs_t deadlineTimestamp;    };    std::vectorinvocations;    {        std::lock_guard lock(mMutex);        auto const now = mTimeKeeper->now();        mLastTimerCallback = now;        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {            auto& callback = it->second;            auto const wakeupTime = callback->wakeupTime();            if (!wakeupTime) {                continue;            }            auto const readyTime = callback->readyTime();            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast(0));            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {                callback->executing();                invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),                                                    *wakeupTime, *readyTime});            }        }        mIntendedWakeupTime = kInvalidTime;        rearmTimer(mTimeKeeper->now());    }    for (auto const& invocation : invocations) {        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,                                      invocation.deadlineTimestamp);    }

              這里的核心邏輯其實就是遍歷mCallbacks,然后分別回調。

              那么mCallbacks是怎么添加的呢? CallbackRepeater創建的時候,回去注冊 mRegistration,同時會傳入CallbackRepeater::callback方法作為回調,所以mCallbacks其實就是CallbackRepeater::callback。

              void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {       ...        mCallback(vsyncTime, wakeupTime, readyTime);        ...    }

              很明顯直接交給mCallback處理,所以我們又得看一下這個mCallback從何而來。

              這個mCallback是CallbackRepeater創建時傳入的DispSyncSource::onVsyncCallback方法:

              mCallbackRepeater =            std::make_unique(vSyncDispatch,                                               std::bind(&DispSyncSource::onVsyncCallback, this,                                                         std::placeholders::_1,                                                         std::placeholders::_2,                                                         std::placeholders::_3),                                               name, workDuration, readyDuration,                                               std::chrono::steady_clock::now().time_since_epoch());

              所以,最終會調用到DispSyncSource::onVsyncCallback方法:

              void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,                                     nsecs_t readyTime) {    VSyncSource::Callback* callback;    {        std::lock_guard lock(mCallbackMutex);        callback = mCallback;    }    ...    if (callback != nullptr) {        callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime});    }}

              又是回調,這里很繞。這里的callback其實就是EventThread,仍然是在創建EventThread的時候設置的:

              EventThread::EventThread(std::unique_ptrvsyncSource,                         android::frametimeline::TokenManager* tokenManager,                         InterceptVSyncsCallback interceptVSyncsCallback,                         ThrottleVsyncCallback throttleVsyncCallback,                         GetVsyncPeriodFunction getVsyncPeriodFunction)      : mVSyncSource(std::move(vsyncSource)),        mTokenManager(tokenManager),        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),        mThrottleVsyncCallback(std::move(throttleVsyncCallback)),        mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),        mThreadName(mVSyncSource->getName()) {    LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,            "getVsyncPeriodFunction must not be null");    mVSyncSource->setCallback(this);    ...}

              所以,終于可以看到盡頭了。最終其實就是調用到EventThread的onVSyncEvent方法:

              void EventThread::onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) {    std::lock_guardlock(mMutex);    LOG_FATAL_IF(!mVSyncState);    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,                                       vsyncData.expectedPresentationTime,                                       vsyncData.deadlineTimestamp));    mCondition.notify_all();}

              這里我們看到,會生成一個VSync信號,加入到mPendingEvents集合中,并且發出通知,讓threadMain去獲取,從而完成了VSync信號的生產者流程。

              畫了如下的surfaceFlinger結構圖,方便理解(非完整版):

              五.APP層收到信號進行刷新

              本章講的主要流程如下圖紅圈所示:

              5.1 APP端接受流程

              3.3小節中講到,SurfaceFlinger會通過BitTube的方式傳遞給APP側Vsync信號。發送vscyn信號的方法在DisplayEventReceiver.cpp中,而接收方法也在這個類當中。而具體調用方則是DisplayEventDispatcher.cpp中的dispatchVsync方法。流程如下圖所示:

              5.2 dispathVsync分發流程

              上面在handleEvent中,processPendingEvents獲取到了Vsync信號VsyncEventData后,交給dispatchVsync方法負責處理。

              dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);

              dispatchVsync方法的實現者是android_view_DisplayEventReceiver.cpp,如下:

              void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,                                               uint32_t count, VsyncEventData vsyncEventData) {    JNIEnv* env = AndroidRuntime::getJNIEnv();    ...    jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData);    env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,                            timestamp, displayId.value, count, javaVsyncEventData);    ...}

              我們可以看到,先是把VsyncEventData轉換為java可以接受的jobject對象,然后通過CallVoidMethod方法通知到java層中DisplayEventReceiver.java中的dispatchVsync方法。

              5.3 java中流程流轉

              首先DisplayEventReceiver中dispatchVsync方法被調用:

              // Called from native code.    @SuppressWarnings("unused")    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,            VsyncEventData vsyncEventData) {        onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);    }

              該方法中直接調用onVsync方法,調用到Choreographer.java中FrameDisplayEventReceiver下的onVsync方法:

              @Override        public void onVsync(long timestampNanos, long physicalDisplayId, int frame,                VsyncEventData vsyncEventData) {            try {                ...                if (timestampNanos > now) {                    timestampNanos = now;                }                if (mHavePendingVsync) {                } else {                    mHavePendingVsync = true;                }                mTimestampNanos = timestampNanos;                mFrame = frame;                mLastVsyncEventData = vsyncEventData;                Message msg = Message.obtain(mHandler, this);                msg.setAsynchronous(true);                mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);            } finally {                Trace.traceEnd(Trace.TRACE_TAG_VIEW);            }        }

              onVsync方法中,主要判斷時間。如果timestampNanos>now,則是用當前時間。所以還是以方法的調用時間為準。然后通過handle轉發到主線程中執行。

              Message.obj=this,本身FrameDisplayEventReceiver又實現了Runnable接口,所以自然會執行FrameDisplayEventReceiver下的run方法:

              @Override        public void run() {            mHavePendingVsync = false;            doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);        }

              這時候我們就看到,執行到了doFrame方法,而這個方法也是就是渲染流程的執行者。

              5.4 如何觸發handleEvent流程?

              講到這里,你或許有個疑問,上面流程中,如何執行到5.1中的handleEvent方法的呢?

              主要是下圖所示的流程:

              如果你斷點調試的時候,會發現下面所示的這個方法,竟然是主線程調用的:

              DisplayEventReceiver.java // Called from native code.    @SuppressWarnings("unused")    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,            long frameTimelineVsyncId, long frameDeadline, long frameInterval) {        onVsync(timestampNanos, physicalDisplayId, frame,                new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval));    }

              為什么用竟然呢?因為一般來看,應該是子線程等待接受SurfaceFlinger的信號,收到了信號后交給主線程處理,如果是主線程去等待,豈不是主線程阻塞了?

              這里使用looper.addFd()方法,在該方法中,用到了一個epoll_ctl的機制,即對FD文件進行監聽,當FD改變時觸發主線程的回調。如果處理完回調任務,則會進入epoll_wait的阻塞,繼續監聽。

              六.擴展問題

              問:高頻次請求vsync信號,會突破60FPS的限制嗎?

              答:不會。

              首先ViewRootImpl中做了一層處理,哪怕16ms改變了很多View的布局,最終執行到了scheduleTraversals方法時,因為有如下的判斷,所以都只會執行一次vsync信號的請求和注冊一次回調,直至收到VSYNC信號。

              void scheduleTraversals() {        if (!mTraversalScheduled) {            mTraversalScheduled = true;            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);        }    }

              取消限制:

              void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;}

              其次,2.2小節中講到,native請求VSYNC信號時也有一次限制,等到VSYNC信號時是不會再次發送請求的。

              status_t DisplayEventDispatcher::scheduleVsync() {    if (!mWaitingForVsync) {        ...        status_t status = mReceiver.requestNextVsync();        ...        mWaitingForVsync = true;        ...    }    return OK;}

              七.參考資料

              https://www.jianshu.com/p/6083c590521b

              https://www.jianshu.com/p/386bbb5fa29a //努比亞技術團隊文章

              八.聲明

              1.本文是原創,根據網上資料和閱讀ASOP的源碼之后得出的結論,如有問題,歡迎指出。

              2.圖中涉及到的流程圖如果想要高清大圖或者pos格式的原圖,可以私信我。

              責任編輯:

              標簽:

              相關推薦:

              精彩放送:

              新聞聚焦
              Top 中文字幕在线观看亚洲日韩