diff --git a/blogs/Android/Framework/Interview/UI 体系相关/UI 刷新机制.md b/blogs/Android/Framework/Interview/UI 体系相关/UI 刷新机制.md new file mode 100644 index 0000000..0cf20cc --- /dev/null +++ b/blogs/Android/Framework/Interview/UI 体系相关/UI 刷新机制.md @@ -0,0 +1,301 @@ +--- +说说 Android 的 UI 刷新机制 +--- + +1. 丢帧一般是什么原因引起的? +2. Android 刷新频率 60 帧/秒,每隔 16 ms 调 onDraw 绘制一次? +3. onDraw 完之后屏幕会马上刷新吗? +4. 如果界面没有重绘,还会每隔 16ms 刷新屏幕吗? +5. 如果在屏幕快要刷新的时候才去 onDraw 绘制会丢帧吗? + +```java +public void requestLayout(){ + scheduleTraversals(); +} +void scheduleTraversals(){ + mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); + if(!mTraversalScheduled){ + mTraversalScheduled = true; + mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); + } +} +// mTraversalRunnable +void doTraversal(){ + if(mTraversalScheduled){ + mTraversalScheduled = false; + performTraversals(); + } +} +``` + +```java +private void postCallbackDelayedInternal(int callbackType, ){ + mCallbackQueues[callbackType].addCallbackLocked(dueTime, ...); + scheduleFrameLocked(now); +} +private void scheduleFrameLocked(long now){ + if(isRunningOnLooperThreadLocked()){ + scheduleVsyncLocked(); + }else{ + Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); + msg.setAsynchronous(true); + mHandler.sendMEssageAtFrontOfQueue(msg); + } +} +``` + +```java +class FrameDisplayEventReceiver extends DisplayEventReceiver{ + public void onVsync(long timestampNanos, int builtInDisplayId, int frame){ + mTimestampNanos = timestampNanos; + mFrame = frame; + Message msg = Message.obtain(mHandler, this); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, timestampNanos/TimeUtils.NANOS_PER_MS); + } + public void run(){ + doFrame(mTimestampNanos, mFrame); + } +} +// Choreographer#doFrame +void doFrame(long frameTimeNanos, int frame){ + 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; + frameTimeNanos = startNanos - lastFrameOffset; + } + + // 处理 Callback + doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); + doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); + doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos); + doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); + doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); +} +void doCallback(int callbackType, long frameTimeNanos){ + CallbackRecord callbacks; + callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(...); + for (CallbackRecord c = callbacks; c != null; c = c.next) { + c.run(frameTimeNanos); + } +} +``` + +![](https://i.loli.net/2020/03/25/GfDFqBLMAvKbNdn.png) + +```java +private void scheduleVsyncLocked(){ + mDisplayEventReceiver.scheduleVsync(); +} +``` + +```c++ +status_t NativeDisplayEventReceiver::scheduleVsync(){ + status_t status = mReceiver.requestNextVsync(); +} +status_t DisplayEventReceiver::requestNextVsync(){ + mEventConnection->requestNextVsync(); +} +DisplayEventReceiver::DisplayEventReceiver(){ + sp sf(ComposerService::slef()); + if(sf!=null){ + mEventConnection = sf->createDisplayEventConnection(); + mDataChannel = mEventConnection->getDataChannel(); + } +} +sp EventThread::Connection::getDataChannel() const{ + return mChannel; +} +``` + +创建 Connection,实现是在 SurfaceFlinger 里: + +```c++ +sp SurfaceFlinger::createDisplayEventConnection(){ + return mEventThread->createEventConnection(); +} +sp EventThread::createEventConnection() const { + return new Connection(const_cast(this)); +} +EventThread::Connection::Connection(const sp&eventThread):count(-1), mEventThread(eventThread), mChannel(new BitTubr()) { + +} +void EventThread::Connection::onFirstRef(){ + mEventThread->registerDisplayEventConnection(this); +} +status_t EventThread::registerDisplayEventConnection(const sp&connection){ + mDisplayEventConnections.add(connection); + mCondition.broadcast(); +} +``` + +创建 EventThread: + +```c++ +void SurfaceFlinger::init(){ + sp vsyncSrc = new DispSyncSource(&mPrimaryDispSync, ...); + mEventThread = new EventThread(vsyncSrc); +} +``` + +```c++ +bool EventThread::threadLoop(){ + DisplayEventReceiver::Event event; + Vector> signalConnections; + signalConnections = waitForEvent(&event); + const size_t count = signalConnection,size(); + for(size_t i = 0;i&conn(signalConnections[i]); + conn->postEvent(event); + } + return true; +} +Vector> EventThread::waitForEvent(...){ + Vector> signalConnections; + do{ + // 看是否已经有 vsync 信号来了 + // 如果有的话,就准备 connection 列表返回 + // 如果没有的话,就等待 vsync 信号 + }while(signalConnections.isEmpty()); + return signalConnections; +} +``` + +```c++ +void EventThread::requestNextVsync(const sp&connection){ + if(connection->count<0){ + connection->count=0; + mCondition.broadcast(); + } +} +``` + +```c++ +status_t EventThread::Connection::postEvent(const DisplayEventReceiver::Event& event){ + ssize_t size = DisplayEventReceiver::sendEvents(mChannel, &event, 1); + return size<0?status_t(size):status_t(NO_ERROR); +} +ssize_t size DisplayEventReceiver::sendEvents(const sp &dataChannel, Event const* events, size_t count) { + return BitTube:sendObjects(dataChannel, events, count); +} +ssize_t BitTube::sendObjects(const sp &tube, ...){ + const char* vaddr = reinterpret_cast(events); + sszie_t size = tube->write(vaddr, count*objSize); + return size<0?size:size/static_cast(objSize); +} +``` + +BitTube 是在 SurfaceFlinger 创建的,读的一端是如何传递给应用进程呢? + +```c++ +DisplayEventReceiver::DisplayEventReceiver(){ + sp sf(ComposerService::getComposerService()); + mEventConnection = sf->createDisplayEventConnection(); + mDataChannel = mEventConnection->getDataChannel(); +} +virtual sp getDataChannel() const{ + Parcel data, reply; + data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); + remote()->transact(GET_DATA_CHANNEL, data, &reply); + return new BitTube(reply); +} +sp EventThread::Connection::getDataChannel() const{ + return mChannel; +} +``` + +应用进程如何监听读的描述符呢? + +```java +private Choreographer(Looper looper){ + mDisplayEventReceiver = new FrameDisplayEventReceiver(looper); +} +public DisplayEventReceiver(Looper looper){ + mReceiverPtr = nativeInit(new WeakReference(this), ...); +} +static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, ...){ + sp receiver = new NativeDisplayEventReceiver(...); + status_t status = receiver->initialize(); + return reinterpret_cast(receiver.get()); +} +status_t NativeDisplayEventReceiver::initialize(){ + mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL); + rwturn OK; +} +DisplayEventReceiver::DisplayEventReceiver(){ + sp sf(ComposerService::getComposerService()); + mEventConnection = sf->createisplayEventConnection(); + mDataChannel = mEventConnection->getDataChannel(); +} +int DisplayEventReceiver::getFd() const{ + return mDataChannel->getFd(); +} +int BitTube::getFd() const{ + return mReceiverFd; +} +int Looper::addFd(int fd, int ident, int events, ...){ + Request request: + request.fd = fd; + struct epoll_event eventItem: + request.initEventItem(&eventItem); + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem); + mRequests.add(fd, request); +} +``` + +Looper 检测 fd 的读事件: + +```java +int Looper::pollInner(int timeoutMillis){ + int eventCount = epoll_wait(mEpollFd, eventItems, ...); + for(int i = 0;ihandleEvent(fd, events, data); + if(callbackResult==0){ + removeFd(fd, response.request.seq); + } + response.request.callback.clear(); + result = POLL_CALLBACK; + } + return result; +} +int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, ...){ + if(processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, ...)){ + mWaitingForVsync = false; + dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); + } + return 1; +} +void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, ...){ + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, ...); +} +void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame){ + onVsync(timestampNanos, buildInDisplayId, frame); +} +``` + +#### 总结 + +1. Vsync 的原理是怎样的? +2. Choreographer 的原理是怎样的? +3. UI 刷新的大致流程,应用和 SurfaceFlinger 的通信过程? \ No newline at end of file diff --git a/images/Android/Framework/Interview/Choreographer.png b/images/Android/Framework/Interview/Choreographer.png new file mode 100644 index 0000000..aaf1cb7 Binary files /dev/null and b/images/Android/Framework/Interview/Choreographer.png differ