Zero, preface,

This paper focuses on:
[1].View#invalidate what is done to trigger the View redraw? [2]. How are views added to viewGroups? [3]. What do ViewGroup and ViewRootImpl do on invalidate kids? [4]. Analyze the difference and relationship between invalidate and postInvalidate in source code level.Copy the code

1.View#invalidatemethods

---->[View#invalidate]-------------------- public void invalidate() { invalidate(true); } ---->[View#invalidate(boolean)]-------------------- * @hide */ public void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); } ---->[View#invalidateInternal]-------------------- void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (mGhostView ! = null) { mGhostView.invalidate(true); return; }... final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p ! = null && ai ! = null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); /// Call invalidateChild() to refresh the View p.invalidatechild (this, damage); }... }} | - pause here this mGhostView is just like a ghost, the View of the eight times, did not assign a value to it at a time Because it is the package probably in other classes, pay attention to here, after all, if he is not empty, painting and then returnCopy the code

2. Who’s my dad? View’s blood

There is no ViewGroup in the whole View, but the interface [ViewParent] is in charge

Here’s a question: Who is the ViewParent implementation class? There is an implementation of ViewGroup, but there is also ViewRootImpl that implements ViewParent. So who is this p?

| - can see p is to undertake mParent local variables, Full-text search mParent = to see him when initializing or assigned - > [View# member variable] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / The parent this view is attached to. - The parent View to which this View is added is protected ViewParent mParent; / / note that access is protected - > [View# assignParent] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - visible assignParent here is the core of the initialization mParent method is void assignParent(ViewParent parent) { if (mParent == null) { mParent = parent; } else if (parent == null) { mParent = null; } else { throw new RuntimeException("view " + this + " being added, but" + " it already has a parent"); }} | - search assignParent hasn't called in the View, that can only say others adjustable | - and the most close View to recognize the old daddy is in the ViewGroup addView, Take a look at -- - > [ViewGroup# addView (View)] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the public void addView (View child) {- > addView (child, - 1); } ---->[ViewGroup#addView(View,int)]----------------------------- public void addView(View child, int index) { ... LayoutParams params = child.getLayoutParams(); if (params == null) { params = generateDefaultLayoutParams(); . } --->addView(child, index, params); } ---->[ViewGroup#addView(View,int,LayoutParams)]----------------------------- public void addView(View child, int index, LayoutParams params) { ... requestLayout(); invalidate(true); --->addViewInner(child, index, params, false); } ---->[ViewGroup#addViewInner]----------------------------- private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { ... --->addInArray(child, index); If (preventRequestLayout) {--> child-.assignParent (this); } else {--> child.mParent = this; // This directly assigns the child's mParent value}... } | - look at ViewRootImpl again now, I am straight, since setView, don't understand, Looked ahead a few relevant content -- - > [ViewRootImpl# setView] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the public void setView (View View, WindowManager.LayoutParams attrs, View panelParentView) { ... view.assignParent(this); } | - at this point of View in previous article analyzed DecorView, which invokes the DecorView assignParent, so is acknowledge ViewRootImpl for torre DecorView, although ViewRootImpl is not the View, But it's a ViewParent so it's ok to be a parent, so does the View's invalidate method take the ViewGroup or the invalidateChild of the ViewRootImpl? A: If it is a ViewGroup, it adds a child View, and the parent of the child View is ViewGroup, and the parent of the child View is ViewGroup. ViewRootImpl is his father, so the invalidate method for DecorView is ViewRootImpl#invalidateChild, so that's why ViewRootImpl is so powerful. In other words, Cooperate with the emperor to make the vassals have. So ViewRootImpl says you don't want to refresh the UI for me in the child thread, and the views just do thatCopy the code

3.ViewGroup#invalidateChildmethods
-- -- -- - > [ViewGroup# invalidateChild] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - ViewGroup as ViewParent implementation class, Public final void invalidateChild(View child, final Rect dirty) {... ViewParent parent = this; if (attachInfo ! = null) { ... } do { View view = null; if (parent instanceof View) { view = (View) parent; }... Find root view / / cycle, and call the invalidateChildInParent () method is the parent. = the parent invalidateChildInParent (location, dirty); if (view ! = null) { ... } } while (parent ! = null); }} | - through the while to traverse this here, perform a invalidateChildInParent method This method returns a ViewParent object, take a look at this method: ---->[ViewGroup#invalidateChildInParent]-------------------- public ViewParent invalidateChildInParent(final int[] location, final Rect dir if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) ! = 0) {... return mParent; } return null; } | - this method looks just set up your own area and placement, and nothing substantial | - but he is the return value of the mParent, namely it yourself tidy, make dad ran out | - so invalidateChild above is always throw the old daddy, Until DecorView | - because dad is ViewRootImpl DecorView, So [the parent instanceof View] does not meet the conditions of | - then call the ViewRootImpl# invalidateChild (ViewGroup fully play of soy sauce was feeling...).Copy the code

4. Draw the update core:ViewRootImpl#invalidateChildmethods

ViewGroup is not powerful, and did not trigger the child drawing method, ViewRootImpl big guy, a move to determine the universe

---->[ViewRootImpl#invalidateChild]-------------------- @Override public void invalidateChild(View child, Rect dirty) { invalidateChildInParent(null, dirty); } ---->[ViewRootImpl#invalidateChildInParent]-------------------- @Override public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); // Highlight... Here the thread is checked. If (dirty == null) {invalidate(); return null; } else if (dirty.isEmpty() && ! mIsAnimating) { return null; }... --->invalidateRectOnScreen(dirty); return null; } ---->[ViewRootImpl#invalidateRectOnScreen]-------------------- private void invalidateRectOnScreen(Rect dirty) { ... if (! mWillDrawSoon && (intersected || mIsAnimating)) { ---> scheduleTraversals(); / / opened up a plan to traverse}} - > [ViewRootImpl# scheduleTraversals] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - Choreographer translation: dance instructor? -- Big guy really knows how to name... void scheduleTraversals() { if (! mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( ---> Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }} -- -- -- -- -- -- -- -- the next leg of don't want to see you can skip, mainly after the incoming Runnable is executed when -- -- -- -- -- - | - Choreographer postCallback core is invoked the following methods: | - basically see into and Runnable, The following action is Runnable -- - > [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); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } } public void addCallbackLocked(long dueTime, Object action, Object token) { CallbackRecord callback = obtainCallbackLocked(dueTime, action, token); . } private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) { CallbackRecord callback = mCallbackPool; if (callback == null) { callback = new CallbackRecord(); } else { mCallbackPool = callback.next; callback.next = null; } callback.dueTime = dueTime; --->callback.action = action; callback.token = token; return callback; } | - visible action flow into CallbackRecord action in the field -- -- -- - > [Choreographer# CallbackRecord] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - visible CallbackRecord run method to trigger the action of the run private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; public void run(long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); }}} | - global searching, the code is not posted, Finally [c.r UN (frameTimeNanos)] in [doCallbacks] approach trigger | - and [doCallbacks] in [doFrame] trigger, [doFrame] trigger when receive handler [MSG_DO_FRAME] --------------------------------------------------------------------------------------- | - get to the point: mTraversalRunnable is a Runnable, through Choreographer# postCallback will eventually be executed | - have a look at what is mTraversalRunnable, Dry what -- -- -- - > [Choreographer# CallbackRecord] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); | - easy to understand and perform doTraversal () operation, as for doTraversal () do... | - in short, doTraversal traverse all the nodes () operation, measurement, layout, drawing, (it to cao cao when also not easy) | - the same analysis and the second time I don't want to write, see: : income and see [- View the surrounding -] framework layer # 3 # 4Copy the code

This solves my puzzle: how invalidate triggers a View redraw.


5. The difference between postInvalidate() and invalidate

---->[View#postInvalidate]----------------------- public void postInvalidate() { postInvalidateDelayed(0); } ---->[View#postInvalidateDelayed]----------------------- public void postInvalidateDelayed(long delayMilliseconds) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo ! = null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); }} | - feeling pretty straightforward, Directly take ViewRootImpl# dispatchInvalidateDelayed -- - > [ViewRootImpl# dispatchInvalidateDelayed] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); } | - Handler by obtainMessage put the view in a MSG_INVALIDATE | in the identity of the Message - if the Handler is not familiar, also please sortie: Android about: War shows that Handler - | - see Handler, first not see handleMessage is how to deal with it, but look at what thread creation Handler in | - that is, the Handler which is in which thread. -- -- -- - > [ViewRootImpl# mHandler] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - was not in the child thread, plus ViewRootImpl is in the main thread is created (don't know to see above), So mHandler is the main thread final ViewRootHandler mHandler = new ViewRootHandler(); @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_INVALIDATE: ((View) msg.obj).invalidate(); // The invalidate of the View is called, and the main thread is broken; | - here is an end, that is to say, Google bosses afraid we are in the child thread invalidate god | - he built a Handler to help us save trouble, as to invalidate or postInvalidate? Do you have to turn a corner when you can get home in a straight line? After all, postInvalidate also triggers View#invalidate and requires an additional message to play. So you can use "postInvalidate" on the main thread, and "postInvalidate" on the subthread. If you think "postInvalidate" is too long, you can create your own Handler.Copy the code

In general, the invalidate method of View is not as complex as I believe. It takes half a day to write…


Postscript: Jie wen standard

1. Growth record and Errata of this paper
Program source code The date of The appendix
V0.1– The 2018-2-23 There is no

Publish name: how much does the invalidate method know about [-view -] source level

Jiwen link: juejin.cn/post/684490…

2. More about me
Pen name QQ WeChat
Zhang Feng Jie te Li 1981462002 zdl1994328

My github: github.com/toly1994328

I’m Jane books: www.jianshu.com/u/e4e52c116… My nuggets: juejin.cn/user/149189… Personal website: www.toly1994.com

3. The statement

1—- this article is originally written by Zhang Feng Jetelie, please note 2—- all programming enthusiasts are welcome to communicate with each other 3—- personal ability is limited, if there is any error, you are welcome to criticize and point out, will be modest to correct 4—- See here, I thank you here for your love and support