View绘制流程

一、View什么时候绘制到屏幕上的

是在onResume之后

1.onResume是通过在ActivityThread的handleResumeActivity方法,我们从handleResumeActivity方法开始分析,View是如何绘制到屏幕上的

    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
......
        // TODO Push resumeArgs into the activity for consideration
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); // ⭐
......

        final Activity a = r.activity;

......
        if (r.window == null && !a.mFinished && willBeVisible) { //⭐
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
}

2.先分析handleResumeActivity方法是如何调用到Activity的omResume方法的

从上面贴出的代码,我们看到handleResumeActivity会调用performResumeActivity这个方法:

    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
......
            r.activity.performResume(r.startsNotResumed, reason);
......
        return r;
    }

然后,调用Activity的PerformResume方法:

    final void performResume(boolean followedByPause, String reason) {
......
        // mResumed is set by the instrumentation
        mInstrumentation.callActivityOnResume(this);
.....
    }

PerformResume方法调用mInstrumentation.callActivityOnResume(this)方法

    public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();
        
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    am.match(activity, activity, activity.getIntent());
                }
            }
        }
    }

callActivityOnResume方法中调用了activity的onResume方法,到此handleResumeActivity方法是如何调用到Activity的omResume方法说清楚了,上面提到了View是在onResume方法后绘制在手机上的,那具体是在哪里,我们继续分析handleResumeActivity的第二个重点代码

4.handleResumeActivity的第二个重点代码

        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();//获取setContentView方法创建的DecorVieww
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager(); //a是Activity,通过调用getWindoManager方
//法获取WindowManager,ViewManager是一个接口,WindowManager实现了ViewManager这个接口
            WindowManager.LayoutParams l = r.window.getAttributes(); // 获取window的LayoutParams
            a.mDecor = decor;
......
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) { // 一个Activity中只会添加一次⭐
                    a.mWindowAdded = true;
                    wm.addView(decor, l); //⭐
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        }

可以看到先通过Activity的getWindowManager方法获取了WindowManager,WindowManager是一个接口它的实现类是WindowManagerImpl,所以wm.addView(decor,l)实际上是执行的WindowManagerImpl的addView方法,通过这个方法的名字也可以猜到是把DecorView和PhoneWindow联系起来的,我们也知道了是在handelResumeActivity方法中把window和View联系起来的。

 在上篇setContentView方法详解一文中我们知道Window是在Activity的attach方法中new出来的,

同样在Activity的attach方法中对WindowManager做的处理:

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {


        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();


}


    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow); //可以看到WindowManager的实现类是WindowManagerImpl
    }

5.分析WindowManagerImpl的addView方法 

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

WindowManagerImpl中的对Window的操作,都是通过WindowManagerGlobal来完成的,这个WindowManagerGlobal是在创建WindowManagerImpl对象的时候创建:

public final class WindowManagerImpl implements WindowManager {

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

}

所以WindowManagerImpl的addView方法实际是调用的WindowManagerGlobal的addView的方法:

    @UnsupportedAppUsage
    private final ArrayList<View> mViews = new ArrayList<View>();
    @UnsupportedAppUsage
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    @UnsupportedAppUsage
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
   
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
......

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
......

        ViewRootImpl root;

            root = new ViewRootImpl(view.getContext(), display);//⭐

            view.setLayoutParams(wparams);

            mViews.add(view); //⭐
            mRoots.add(root); //⭐
            mParams.add(wparams); //⭐

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView, userId); //⭐
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

一个Activity中wm.addView方法只会执行一次,ViewRootImpl是在addView方法中new出来的,所以一个Activity中只会有一个ViewRootImpl,在WindowManagerGlobal中维护着三个ArrayList集合,mViews是用来存储DecorView,mRoots是用来存储ViewRootImpl,mParams是用来存储window的LayoutParams,WindowManagerGlobal是一个单例,它是一个进程所有的窗口的管理类,WindowManagerGlobal通过ViewRootImpl是和WMS交互

所以我们知道了,一个Activity对应一个Window(在Activity的attach方法中创建的PhoneWindo),一个Window对应一个WindowManagerImpl对象(一个window对应一个windowManager的实例,windowManager的实现类就是WindowManagerImpl),一个WindowManagerImpl对应一个ViewRootImpl,ViewRootImpl是和WMS交互来管理自己的窗口,WindowManagerGlobal是一个单例,它是一个进程所有的窗口的管理类,WindowManagerGlobal通过ViewRootImpl是和WMS交互的,View的绘制、事件分发都由它来控制

 6.分析ViewRootImpl的构造方法

    public ViewRootImpl(Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
        mContext = context;
        mWindowSession = session; // 从wMS中创建的一个session对象,可以当成WMS的一个代理
......
        mThread = Thread.currentThread(); // ⭐这个mThread后面要用到
......
        mDirty = new Rect();
......

        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                context);// 用来保存当前窗口信息
......
        mChoreographer = useSfChoreographer // 编舞者
                ? Choreographer.getSfInstance() : Choreographer.getInstance();
......                              
    }

7.分析ViewRootImpl的setView方法

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
......
               requestLayout(); // ⭐真正开始绘制流程,单独分析
......
               res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
        mAttachInfo.mDisplayCutout, inputChannel,
        mTempInsets, mTempControls); // 将窗口添加到WMS上面
......
        mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
        Looper.myLooper()); // 接收事件
......
        view.assignParent(this);  // view是DecorView,这个是把DecorView的父亲设置成当前的ViewRootImpl,
//DecorView已经是View树的顶层,现在在上面加了一层,ViewRootImpl变成了View树的顶层了
            }

        }
    }

总结:到此其实是View树的根DecorView是如何添加到Window上的流程,首先我们知道了是在ActivityThread的handleResumeActivity方法中且在Activity的onResume方法后添加的,从本质上来说DecorView其实并没有真正的像View那样Draw在Window上,而是通过WindowManagerImpl最终调用WindowManagerGlobal的addView方法,在这个方法中创建了一个VIewRootImpl作为DecorView的父亲,并且来管理DecorVIew(也就包括View树)的绘制、事件处理等,所以phoneWIndow通过WindowManager来管理ViewRootImpl,ViewRootImpl和WMS打交道来管理View和事件的处理。

8.分析ViewRootImpl的requestLayout方法,真正的View绘制流程

    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread(); // ⭐
            mLayoutRequested = true;
            scheduleTraversals(); // ⭐
        }
    }

先分析checkThread:

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

mThread上面提到了,在ViewRootImpl的构造方法中获取的当前线程,ViewRootImpl默认是在主线程创建的,所以我们对UI的操作都必须在主线程,这样能够保证操作UI时的线程安全,这个才是为什么一定要在主线程绘制UI的本质,换句话说,如果在子线程创建的ViewRootImpl那对UI的操作必须在这个子线程进行,总之ViewRootImpl在哪个线程创建就只能在哪个线程更新UI

9.分析ViewRootImpl的ScheduleTraversals方法

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//handler机制的异步消息屏障开启
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

mTraversalRunable是要执行的任务:

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();


    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//移除异步消息屏障

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals(); //开始绘制,⭐下面重点分析

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

10.分析ViewRootImpl中的performTraversals

    private void performTraversals() {
......
    / Ask host how big it wants to be
    windowSizeMayChange |= measureHierarchy(host, lp, res,
    desiredWindowWidth, desiredWindowHeight); //预测量🌟
......
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//测量🌟
......
    performLayout(lp, mWidth, mHeight);//布局🌟
......
    performDraw(); //从画布上画出来🌟
    }

(1).预测量

    private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
            final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
        int childWidthMeasureSpec;
        int childHeightMeasureSpec;
        boolean windowSizeMayChange = false;
......

        boolean goodMeasure = false;
        if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {//🌟
            // On large screens, we don't want to allow dialogs to just
            // stretch to fill the entire width of the screen to display
            // one line of text.  First try doing the layout at a smaller
            // size to see if it will fit.
            final DisplayMetrics packageMetrics = res.getDisplayMetrics();
            res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
            int baseSize = 0;
            if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
                baseSize = (int)mTmpValue.getDimension(packageMetrics);
            }
            if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
                    + ", desiredWindowWidth=" + desiredWindowWidth);
            if (baseSize != 0 && desiredWindowWidth > baseSize) { 
                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);// 第一次🌟
                if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                        + host.getMeasuredWidth() + "," + host.getMeasuredHeight()
                        + ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
                        + " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
                if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                    goodMeasure = true;
                } else {
                    // Didn't fit in that size... try expanding a bit.
                    baseSize = (baseSize+desiredWindowWidth)/2;
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
                            + baseSize);
                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//第二次🌟
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                    if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                        if (DEBUG_DIALOG) Log.v(mTag, "Good!");
                        goodMeasure = true;
                    }
                }
            }
        }

        if (!goodMeasure) {
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//第三次🌟
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                windowSizeMayChange = true;
            }
        }

        return windowSizeMayChange;
    }

预测量是给Window的LayoutParams是wrapContent的时候才会触发:

lp.width == ViewGroup.LayoutParams.WRAP_CONTENT

首先给Window一个系统默认定好的大小320dp:

final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
    baseSize = (int)mTmpValue.getDimension(packageMetrics);
}

然后进行第一次测量:

childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

如果发现默认的320dp大小够用就结束预测量,如果发现不够用,就扩为(当前大小 + 屏幕大小)/2

,然后再次进行测量:

if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
    goodMeasure = true;
} else {
    // Didn't fit in that size... try expanding a bit.
    baseSize = (baseSize+desiredWindowWidth)/2;
    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
            + baseSize);
    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
    if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
        if (DEBUG_DIALOG) Log.v(mTag, "Good!");
        goodMeasure = true;
    }

如果还是不够用,就直接把屏幕大小作为Window的大小,再次进行测量,这时不管够不够用都没办法了,所以只能结束:

if (!goodMeasure) {
    childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
    childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
        windowSizeMayChange = true;
    }
} 

 所以总结一下预测量,一般是Dialog或者是PopupWindow会用到,预测量最多测量三次,加上后面还会真正的测量一次,所以在绘制流程中,最多会测量四次

(2).测量performMeasure

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

主要就是调用mView.measure,其实mView就是DecorView,DecorView是一个FrameLayout,也就是ViewGroup,所以从这里就开始进入View体系的测量:

对自定义View的Measure和onMeasure的一点心得_AllenC6的博客-CSDN博客_measure和onmeasure写的好的文章:https://www.cnblogs.com/yishujun/p/5560838.htmlhttps://blog.csdn.net/a396901990/article/details/36475213?utm_source=tuicool&utm_medium=referralhttps://blog.csdn.net/dmk877/article/det...https://blog.csdn.net/m0_37707561/article/details/102859277

(3).布局performLayout

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        mScrollMayChange = true;
        mInLayout = true;

        final View host = mView;
        if (host == null) {
            return;
        }
        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
            Log.v(mTag, "Laying out " + host + " to (" +
                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
        }

        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());//🌟
......

     }

host是DecorView,实际是调用ViewGroup的layout方法,所以从这里就进入了View的布局体系:

自定义View对layout、onLayout的一点心得_AllenC6的博客-CSDN博客https://blog.csdn.net/dmk877/article/details/49632959https://blog.csdn.net/a396901990/article/details/38129669推荐大家把第一个小例子做一下:public class MyViewGrop extends ViewGroup { int scrrenWid...https://blog.csdn.net/m0_37707561/article/details/102860921

(3).绘制performDraw

    private void performDraw() {
......
 boolean canUseAsync = draw(fullRedrawNeeded);
......
    }

主要是执行的ViewRootImpl的draw方法:

    private boolean draw(boolean fullRedrawNeeded) {
......
        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
            if (isHardwareEnabled()) { //🌟 硬件加速模式下的绘制
                // If accessibility focus moved, always invalidate the root.
                boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
                mInvalidateRootRequested = false;

                // Draw with hardware renderer.
                mIsAnimating = false;

                if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
                    mHardwareYOffset = yOffset;
                    mHardwareXOffset = xOffset;
                    invalidateRoot = true;
                }

                if (invalidateRoot) {
                    mAttachInfo.mThreadedRenderer.invalidateRoot();
                }

                dirty.setEmpty();

                // Stage the content drawn size now. It will be transferred to the renderer
                // shortly before the draw commands get send to the renderer.
                final boolean updated = updateContentDrawBounds();

                if (mReportNextDraw) {
                    // report next draw overrides setStopped()
                    // This value is re-sync'd to the value of mStopped
                    // in the handling of mReportNextDraw post-draw.
                    mAttachInfo.mThreadedRenderer.setStopped(false);
                }

                if (updated) {
                    requestDrawWindow();
                }

                useAsyncReport = true;

                mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);//🌟这一句是硬件模式下主要绘制逻辑
            } else {//🌟 没有开启硬件加速,软件绘制
                // If we get here with a disabled & requested hardware renderer, something went
                // wrong (an invalidate posted right before we destroyed the hardware surface
                // for instance) so we should just bail out. Locking the surface with software
                // rendering at this point would lock it forever and prevent hardware renderer
                // from doing its job when it comes back.
                // Before we request a new frame we must however attempt to reinitiliaze the
                // hardware renderer if it's in requested state. This would happen after an
                // eglTerminate() for instance.
                if (mAttachInfo.mThreadedRenderer != null &&
                        !mAttachInfo.mThreadedRenderer.isEnabled() &&
                        mAttachInfo.mThreadedRenderer.isRequested() &&
                        mSurface.isValid()) {

                    try {
                        mAttachInfo.mThreadedRenderer.initializeIfNeeded(
                                mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
                    } catch (OutOfResourcesException e) {
                        handleOutOfResourcesException(e);
                        return false;
                    }

                    mFullRedrawNeeded = true;
                    scheduleTraversals();
                    return false;
                }

                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {//🌟这一句是软件模式下主要绘制逻辑
                    return false;
                }
            }
        }

        if (animating) {
            mFullRedrawNeeded = true;
            scheduleTraversals();
        }
        return useAsyncReport;
    }

 绘制流程差不多,我们主要说没有开启硬件加速模式下的绘制流程,最后再比较一下差异:

软件模式下的绘制流程:

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
......
         mView.draw(canvas);
......
    }

最后还是回到调用DecorView的draw方法上:

自定义View对draw和onDraw方法的一点心得_AllenC6的博客-CSDN博客1.View绘制过程的传递是通过dispatchDraw来实现的,dispatchDraw调用子view的draw方法,draw方法主要有这几步:(1)绘制背景background.draw(canvas)(2)绘制自己onDraw(3)绘制children(dispatchDraw)(4)绘制装饰(onDrawScrollBars)这样draw方法调用dispatchDraw...https://blog.csdn.net/m0_37707561/article/details/103386281硬件加速绘制和非硬件加速绘制的区别:

硬件加速绘制:

 

 非硬件加速绘制:

 

二、requestLayout和invalidate的区别

requestLayout流程:

invalidate流程:

 

 流程类似,都是向上调用VIewRootImpl的doTraversals方法,只是inValidate通过设置flag只用执行draw部分的逻辑,而RequestLayout从预测量开始都要执行一遍。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值