Links to related articles:

1. Android Framework – Learning Launcher

2. Source code reading analysis – Window underlying principle and system architecture

Related source files:

/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.cppCopy the code

1. Summarize

Before you start reading, I want you to think about a few questions:

  • How does interface stutter work?
  • How does viewrotimpl communicate with SurfaceFlinger?
  • Will invalidate/requestLayout refresh the screen immediately?
  • SurfaceView/GLSurfaceView

After years of working with Android, we should know a little about VSync signals, but we don’t really understand how they work. Like where does the VSync signal come from? Where to send it? What does it do? This article mainly explains where to send, as for where to come from you can see the previous content.

2. Request the VSync signal

If we need to make a change to our interface, we will usually come to the requestLayout method of ViewRootImpl, either manually or passively. In this method, we will actively request to receive VSync signal. The next time the VSync signal comes, it will automatically come back and start the actual drawing process.

@Override public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } } void scheduleTraversals() { if (! mTraversalScheduled) { mTraversalScheduled = true; // Insert a message barrier mTraversalBarrier = mhandler.getlooper ().getQueue().postsyncBarrier (); / / post a 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) {/ / whether the Choreographer of worker threads if (isRunningOnLooperThreadLocked ()) {scheduleVsyncLocked (); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { ... }}} / / request to receive the next private VSync signal void scheduleVsyncLocked () {mDisplayEventReceiver. ScheduleVsync (); }Copy the code

The scheduleVsync method is actively called each time the requestLayout method is called to receive the next VSync signal. This means that calling the requestLayout method n times in a row will not trigger a draw refresh until the next VSync signal.

3. Receive VSync signals

How does the SurfaceFlinger service send the VSync signal to our App if the App requests the next VSync signal? This starts with the initialization of the DisplayEventReceiver, involves cross-process communication and involves the source code of the Native layer.

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 at the receiving end is added to the stars int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,this, NULL); return OK; } / / across processes create a mEventConnection object DisplayEventReceiver: : DisplayEventReceiver () {sp < ISurfaceComposer > sf(ComposerService::getComposerService()); if (sf ! = NULL) { mEventConnection = sf->createDisplayEventConnection(); if (mEventConnection ! = NULL) { mDataChannel = mEventConnection->getDataChannel(); }}} / / get the receiver fd int DisplayEventReceiver: : getFd () const {the if (mDataChannel = = NULL) return NO_INIT; return mDataChannel->getFd(); } / / VSync signal to callback to this method 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 ()) {env->CallVoidMethod(receiverobj.get (), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count); }} @override public void onVsync(long timestampNanos, int builtInDisplayId, int frame) Really start to refresh the drawing process of mTimestampNanos = timestampNanos; mFrame = frame; Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); }Copy the code

DisplayEventReceiver creates a Connection with SurfaceFlinger when initialized. When the application initializes requestNextVsync, When the next VSync signal comes, SurfaceFlinger will actively notify our App and fall back to the Java layer’s onVsync method to start the actual drawing process.

Video address: pan.baidu.com/s/1tQ7omRNg… Video password: 6HLC