Read The Fucking Source Code

The introduction

Android custom controls involve the process of drawing and distributing views

Android Q — API 29

[Android drawing process]

1. requestLayout

1.1 requestLayout Process details

1.2 requestLayout from bottom up

1.2.1 Let’s look at the ViewrequestLayout()methods

public void requestLayout() { if (mMeasureCache ! = null) mMeasureCache.clear(); If (mAttachInfo! = null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot ! = null && viewroot.isinLayout ()) {//ViewRootImpl will intercept the processing layout (already in process), if (! viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } / / the View tag set, need to layout mPrivateFlags | = PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; PFLAG_FORCE_LAYOUT if (mParent! = null && ! mParent.isLayoutRequested()) { mParent.requestLayout(); } if (mAttachInfo ! = null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; }}Copy the code

1.2.2 Let’s look at ViewRootImplrequestLayoutDuringLayout()methods

Fase: interrupts the layout request; fase: interrupts the layout request. Ture: The layout request continues. boolean requestLayoutDuringLayout(final View view) { if (view.mParent == null || view.mAttachInfo == null) { // Would not normally trigger another layout, so just let it pass through as usual return true; } // If the view being laid out contains the request view, add if (! mLayoutRequesters.contains(view)) { mLayoutRequesters.add(view); } // If (! mHandlingLayoutInLayoutRequest) { // Let the request proceed normally; it will be processed in a second layout pass // if necessary return true; } else { // Don't let the request proceed during the second layout pass. // It will post to the next frame instead. return false; }}Copy the code

1.2.3 The requestLayout method is not copied in the ViewGroup, so the ViewGroup calls the same method as the View

1.2.4 Let’s look at ViewRootImplrequestLayout()methods

public void requestLayout() { if (! MHandlingLayoutInLayoutRequest) {/ / thread inspection checkThread (); mLayoutRequested = true; // Request the Vsync signal to trigger the execution of scheduleTraversals(); }}Copy the code

1.2.5 requestLayout()summary

RequestLayout event layers are passed up to the DecorView (root View), which in turn is passed to the ViewRootImpl (which takes over the ViewParent function of the DecorView). The requestLayout event of the child View will be received and processed by the ViewRootImpl. This is a chain of responsibility pattern, that is, passing the event up until it finds a superior that can handle the event. As the diplomatic steward of the View, only ViewRootImpl can handle requestLayout events.

2. invalidate

2.1 Overview of the Invalidate process

2.2 Invalidate bottom-up

2.2.1 Let’s look at the Viewinvalidate()methods

public void invalidate() {
        invalidate(true);
    }

public void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }
Copy the code

2.2.2 Let’s look at the ViewinvalidateInternal()methods

Void invalidateInternal(int L, int t, int r, int b, Boolean invalidateCache, Boolean fullInvalidate) {void invalidateInternal(int L, int t, int r, int b, Boolean invalidateCache, Boolean fullInvalidate) If (skipInvalidate()) {return; if (skipInvalidate()) {return; } // Reset content capture caches mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; mCachedContentCaptureSession = null; // Determine whether the child View needs to be redrawn according to the mark bit of the View. So there is no need to redraw the if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) = = (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) | | (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) ! = PFLAG_INVALIDATED || (fullInvalidate && isOpaque() ! = mLastIsOpaque)) { if (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } / / set PFLAG_DIRTY labeling (increase the dirty mark) mPrivateFlags | = PFLAG_DIRTY; / / if the cache invalidation, it is clear the corresponding tag the if (invalidateCache) {mPrivateFlags | = PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; // Pass the redrawn area to the parent container if (p! = null && ai ! = null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); p.invalidateChild(this, damage); } // code omitted...... }}Copy the code

2.2.3 Let’s look at ViewGroupinvalidateChild()methods

@deprecated (Please note this Deprecated sign, @override public final void invalidateChild(View child, final Rect dirty) { final AttachInfo attachInfo = mAttachInfo; // If hardware acceleration is enabled (which is enabled by default) // Now you can understand why this method is not deprecated, because it is optimized for hardware acceleration. Details will be covered later. if (attachInfo ! = null && attachInfo. MHardwareAccelerated) {/ / HW accelerated fast path / / further processing, using a new method instead of the abandoned method execution. onDescendantInvalidated(child, child); return; } // code omitted...... / / if the hardware acceleration to shut down, would the previous version of the recursive traversal process, this process is not talked about, is online most of the blog in this version stage) parent = parent. InvalidateChildInParent (location, dirty); // code omitted...... }Copy the code

2.2.4 Let’s look at the ViewGroup onDescendantInvalidated()methods

// Code optimized for hardware acceleration, Public void onDescendantInvalidated(@nonnull View child, @nonnull View target) {/* * HW-only, Rect-ignoring damage codepath * * We don't deal with rectangles here, since RenderThread native code computes damage for * everything drawn by HWUI (and SW layer / drawing cache doesn't keep  track of damage area) */ // if set, combine the animation flag into the parent mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION); if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) ! = 0) { // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential // optimization in provides in a DisplayList world. mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; // simplified invalidateChildInParent behavior: clear cache validity to be safe... mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } / /... and mark inval if in software layer that needs to repaint (hw handled in native) if (mLayerType == LAYER_TYPE_SOFTWARE) { // Layered parents should be invalidated. Escalate to a full invalidate (and note that // we do this after consuming any relevant flags from the originating descendant) mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY; target = this; } // recursively invoking onDescendantInvalidated will eventually invoke the method in view loop. if (mParent ! = null) { mParent.onDescendantInvalidated(this, target); }}Copy the code

2.2.5 Let’s look at ViewRootImplonDescendantInvalidated()methods

public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) { // TODO: Re-enable after camera is fixed or consider targetSdk checking this // checkThread(); if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) ! = 0) { mIsAnimating = true; } // draw method invalidate(); }Copy the code

2.2.6 Let’s look at ViewRootImplinvalidate()methods

void invalidate() { mDirty.set(0, 0, mWidth, mHeight); if (! MWillDrawSoon) {// Draw request, register Vsync signal, wait for drawing refresh report. scheduleTraversals(); }}Copy the code

2.2.7 invalidatesummary

When a child View calls the invalidate method, it adds a marker bit to the View and keeps asking for a refresh from the parent, which calculates the area it needs to redraw until it passes it to the View wrootimpl. Finally, the performTraversals method is triggered to start the View tree repainting process (only the views that need to be repainted).

2.2.8 postInvalidateIntroduction to the

PostInvalidate works the same as invalidate, except that postInvalidate can be invoked in a child thread to request a UI refresh. Why is that? Because the request for relayout and drawing is ultimately handled in the ViewRootImpl, the ViewRootImpl does thread checking (whether it is a UI thread) in the request method. When there’s a request to draw in a child view, postInvalidate. In fact, postInvalidate function is a thread switch + invalidate call.

3 Problem Thinking

What is the difference between requestLayout and InvalDate?

  • RequestLayout and InValidate trigger the entire drawing process. However, in the process of measure and layout, only the situation where the flag is set to FORCE_LAYOUT will be remeasured and layout, and draw will only redraw areas with the flag as dirty.
  • RequestLayout is used to set the FORCE_LAYOUT flag and invalidate is used to set the dirty flag. So requestLayout only triggers measure and layout, and Invalidate only triggers draw.
  • So it’s usually a combination. For example, invalidate is called whenever a refresh is required, requestLayout is called whenever a new measure is required, followed by invalidate.

Xiaobian extension links

Android View Module Family Bucket