Skip to content

Instantly share code, notes, and snippets.

@jieyu
Created March 9, 2013 21:24
Show Gist options
  • Save jieyu/5125806 to your computer and use it in GitHub Desktop.
Save jieyu/5125806 to your computer and use it in GitHub Desktop.
Describe how View.invalidate() is processed in Android to refresh a widget.

How View.invalidate() is processed in Android

This document is based on the code of android-4.1.1_r6 (Jelly Bean).

First, the invalidate() call will be propagated back to the root of the view hierarchy. During the propagation, the system determines the dirty area that needs to be redrawn. The propagation will eventually reach ViewRootImpl.

frameworks/base/core/java/android/view/View.java

10219   void invalidate(boolean invalidateCache) {
...         ...
10250       p.invalidateChild(this, r);
...         ...
10253   }

frameworks/base/core/java/android/view/ViewGroup.java

4000    public final void invalidateChild(View child, final Rect dirty) {
...         ...
4066        parent = parent.invalidateChildInParent(location, dirty);
...         ...
4082    }

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

866     public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
...         ...
904         scheduleTraversals();
...         ...
908     }

Finally, ViewRootImpl will invoke the method scheduleTraversals() to schedule a traversal over the view hierarchy to redraw the invalidated (dirty) area. The tricky part here is that the traversal will not be executed immediately. The system wants the traversal to be in sync with the hardware virtical synchronization events (vsync) in order to address the display tearing problem. For more details about what is vsync and why vsync is needed, please refer to this link.

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

968     void scheduleTraversals() {
969         if (!mTraversalScheduled) {                                              
970             mTraversalScheduled = true;                                          
971             mTraversalBarrier = mHandler.getLooper().postSyncBarrier();          
972             mChoreographer.postCallback(                                         
973                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 
974             scheduleConsumeBatchedInput();                                       
975         }   
976     }

To schedule a traversal, the system first posts barrier to the main event queue. The barrier will prevent other events from being dispatched until the actual traversal is performed. And then, it invokes mChoreographer.postCallback to schedule a traversal with mChoreographer, a coordinator which coordinates the timing of animations, input and drawing (link).

The Choreographer will then schedule a DO_FRAME callback to be invoked in sync with the underlying display vsync events.

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

295     private void postCallbackDelayedInternal(int callbackType, ...) {
...         ...
309         scheduleFrameLocked(now);
...         ...
317     }

453     private void scheduleFrameLocked(long now) {
...         ...
465         scheduleVsyncLocked();
...         ...
482     }

588     private void scheduleVsyncLocked() {
589         mDisplayEventReceiver.scheduleVsync();
590     }

The member field mDisplayEventReceiver is an instance of class FrameDisplayEventReceiver extended from class DisplayEventReceiver. The method scheduleVsync() will eventually call a native method nativeScheduleVsync() in class DisplayEventReceiver.

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

105     public void scheduleVsync() {
...         ...
110         nativeScheduleVsync(mReceiverPtr);
...         ...
112     }

The native function implementation can be found in frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp. The receiver will register a file descriptor with the native event queue during initialization. Once there is a vsync event, the native event queue will invoke a callback handleEvent which will then invoke a Java method onVsync defined in class DisplayEventReceiver (can be overrided) if a vsync is scheduled lately (through nativeScheduleVsync).

frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp

123     int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
...         ...
146         mWaitingForVsync = false;
...         ...
151         env->CallVoidMethod(mReceiverObjGlobal,
152             gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount);
...         ...
157     }

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

680     public void onVsync(long timestampNanos, int frame) {
...         ...
701         mTimestampNanos = timestampNanos;                                     
702         mFrame = frame;
703         Message msg = Message.obtain(mHandler, this);                         
704         msg.setAsynchronous(true);
705         mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
706     }

Finally, the method doFrame() will be invoked. It will then invoke doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); which will finally invoke doTraversal() in ViewRootImpl.

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

484     void doFrame(long frameTimeNanos, int frame) {
...         ...
525         doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
...         ...
533     }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment