Android | 理解 ViewRootImpl

简介: Android | 理解 ViewRootImpl

简介


ViewRootImpl 是 View 的最高层级,是所有 View 的根。ViewRootImpl 实现了 View 和 WindowManager 之间所需要的协议。ViewRootImpl 的创建过程是从 WindowManagerImpl 中开始的。View 的测量,布局,绘制以及上屏,都是从 ViewRootImpl 中开始的。


我们通过一张图来认识一下它:


0a2653c851af460fa595bd959398a8f1.png


Window


我们知道界面中所有的元素都是由 View 构成的,View 是依附于 Window 上面的。Window 只是一个抽象概念,把界面抽象成一个 窗口,也可以抽象成一个 View。


ViewManange


一个接口,内部定义了三个方法,用来对 VIew 的添加,更新和删除。


public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}


WindowManager


也是一个接口,继承自 ViewManager,在应用程序中,通过 WindowManager 来管理 Window,将 View 附加到 Window 上。他有一个实现类 WindowManagerImpl。


WindowManagerImpl


WindowManager 的实现类,WindowManagerImpl 中的内部方法实现都是通过代理类 WindowManagerGlobal 来完成。


WindowManagerGlobal


WindowManagerGlobal 是一个单例,也就是说一个进程中只有一个 WindowManagerGlobal对象,他服务与所有页面的 View。


ViewParent


一个接口,定义了将成为 View 父级类的职责。


ViewRootImpl


视图层次结构的顶部。一个 Window 对应着一个 ViewRootImpl 和 一个 VIew。这个 View 就是被 ViewRootImpl 操作的。


一个小栗子,我们都只 Actiivty 中 会创建一个 Window 对象。 setContentView 方法中的 View 最终也会被添加到 Window 对象中的 DecorView 中,也就是说一个 Window 中对应着一个 View。这个 View 是被 RootViewImpl 操作的。


WindowManager 就是入口。通过 WindowManager 的 addView 添加一个 Window(也可以理解为 View),然后就会创建一个 ViewRootImpl,来对 view 进行操作,最后将 View 渲染到屏幕的窗口上。


例如 Activity 中,在 onresume 执行完成后,就会获取 Window 中的 DecorView,然后通过 WindowManager 把 DecorView 添加到窗口上,这个过程中是由 RootViewImpl 来完成测量,绘制,等操作的。


如果对 Window,WindowManager 不太熟悉可以先看一下这篇文章


RootViewImpl 的初始化


View 的三大流程都是通过 RootViewImpl 来完成的,在 ActivityThread 中,当 Activity 对象被创建完毕后,在 onResume 后,就会通过 WindowManager 将 DecorView 添加到窗口上,在这个过程中会创建 ViewRootImpl:


ActivityThread.handleResumeActivity


public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // .....
    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;
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                //添加 decor 到 window 中
                wm.addView(decor, l);
            } else {
                a.onWindowAttributesChanged(l);
            }
        }
    } 
    //....
}


WindowManagerImpl.addView


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


WindowManagerGlobal.addView


public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    //检查参数是否合法
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    //.....
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
  //创建 ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        //将 Window 所对应的 View,ViewRootImp,params 顺序添加到列表中,这一步是为了方便更新和删除 View
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        // do this last because it fires off messages to start doing things
        try {
            //把 Window 对应的 View 设置给创建的 ViewRootImpl
            //通过 ViewRootImpl 来更新界面并添加到 WIndow中。
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}


ViewRootImpl 对 View 进行操作


ViewRootImpl.setView


public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            attrs = mWindowAttributes;
        //请求布局,执行 View 的绘制方法
            requestLayout();
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = new InputChannel();
            }
            mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                    & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                //将 Window 添加到屏幕上,mWindowSession 实现了 IWindowSession接口,是 Session 的 Binder 对象。
                // addToDisplay 是一次 AIDL 的过程,通知 WindowManagerServer 添加 Window
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            }
    //设置当前 View 的 Parent
            view.assignParent(this);
        }
    }
}


ViewRootImpl.requestLayout


//请求布局
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
//检测当前线程,如果不是主线程,直接抛出异常
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }
        performTraversals();
        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}


在上面代码中,调用 requestLayout 请求布局,最终会执行到 performTraversals 方法中。在这个方法中会调用 View 的 measure() ,layout() ,draw() 方法。


ViewRootImpl. performTraversals


private void performTraversals() {
    // cache mView since it is used so much below...
    final View host = mView;
  //想要展示 Window 的宽高
    int desiredWindowWidth;
    int desiredWindowHeight;
  //第一次执行
    if (mFirst) {
  //....
      //将窗口信息附加到 View 上。
        host.dispatchAttachedToWindow(mAttachInfo, 0);
  //....
    } else {
        desiredWindowWidth = frame.width();
        desiredWindowHeight = frame.height();
        if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
            if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
            mFullRedrawNeeded = true;
            mLayoutRequested = true;
            windowSizeMayChange = true;
        }
    }
  //开始进行布局准备
    boolean windowShouldResize = layoutRequested && windowSizeMayChange
        && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
            || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                    frame.width() < desiredWindowWidth && frame.width() != mWidth)
            || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                    frame.height() < desiredWindowHeight && frame.height() != mHeight));
    windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
    windowShouldResize |= mActivityRelaunched;
    final boolean computesInternalInsets =
            mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
            || mAttachInfo.mHasNonEmptyGivenInternalInsets;
    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
        mForceNextWindowRelayout = false;
  //....
        if (!mStopped || mReportNextDraw) {
            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                    (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
            if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                    || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                    updatedConfiguration) {
                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                if (measureAgain) {
      //View 的测量
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                }
                layoutRequested = true;
            }
        }
    }
  //..........
    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    boolean triggerGlobalLayoutListener = didLayout
            || mAttachInfo.mRecomputeGlobalAttributes;
    if (didLayout) {
        // View 的布局
        performLayout(lp, mWidth, mHeight);
  //.....
    }
  //......
    boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
    if (!cancelDraw) {
        // View 的绘制
        performDraw();
    } else {
        if (isViewVisible) {
            // Try again
            scheduleTraversals();
        } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
                mPendingTransitions.get(i).endChangingAnimations();
            }
            mPendingTransitions.clear();
        }
    }
    mIsInTraversal = false;
}


View 的测量


ViewRootImpl 调用 performMeasure 执行 Window 对应的 View 测量。


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);
    }
}


在 performMeasure 方法中就会执行 View 的 measure 方法,在其中会计算一下约束信息,然后就会调用 onMeasure 方法,


View 的布局


ViewRootImpl 调用 performLayout 执行 Window 对应 View 的布局


private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mScrollMayChange = true;
    mInLayout = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // 调用 layout 方法进行布局
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        mInLayout = false;
        int numViewsRequestingLayout = mLayoutRequesters.size();
        if (numViewsRequestingLayout > 0) {
            ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                    false);
            if (validLayoutRequesters != null) {
                mHandlingLayoutInLayoutRequest = true;
                // Process fresh layout requests, then measure and layout
                int numValidRequests = validLayoutRequesters.size();
                for (int i = 0; i < numValidRequests; ++i) {
                    final View view = validLayoutRequesters.get(i);
                    Log.w("View", "requestLayout() improperly called by " + view +
                            " during layout: running second layout pass");
                    // 请求对该 View 进行布局,该操作会导致此 View 被重新测量,布局,绘制
                    view.requestLayout();
                }
                measureHierarchy(host, lp, mView.getContext().getResources(),
                        desiredWindowWidth, desiredWindowHeight);
                mInLayout = true;
                host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
                mHandlingLayoutInLayoutRequest = false;
                // Check the valid requests again, this time without checking/clearing the
                // layout flags, since requests happening during the second pass get noop'd
                validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                if (validLayoutRequesters != null) {
                    final ArrayList<View> finalRequesters = validLayoutRequesters;
                    // Post second-pass requests to the next frame
                    getRunQueue().post(new Runnable() {
                        @Override
                        public void run() {
                            int numValidRequests = finalRequesters.size();
                            for (int i = 0; i < numValidRequests; ++i) {
                                final View view = finalRequesters.get(i);
                                // 请求对该 View 进行布局,该操作会导致此 View 被重新测量,布局,绘制
                                view.requestLayout();
                            }
                        }
                    });
                }
            }
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}


通过上面代码可以看出,在 layout 布局期间,有可能会对 View 进行 requestLayout 重新进行测量,布局,绘制。


View 的绘制


ViewRootImpl 通过调用 performDraw 执行 Window 对应 View 的绘制。


private void performDraw() {
    if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
        return;
    } else if (mView == null) {
        return;
    }
    final boolean fullRedrawNeeded =
            mFullRedrawNeeded || mReportNextDraw || mNextDrawUseBlastSync;
    mFullRedrawNeeded = false;
    mIsDrawing = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
    boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
    addFrameCallbackIfNeeded();
    try {
        //绘制
        boolean canUseAsync = draw(fullRedrawNeeded);
        if (usingAsyncReport && !canUseAsync) {
            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
            usingAsyncReport = false;
        }
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
  //...
}


private boolean draw(boolean fullRedrawNeeded) {
    //........
  //通知 View 上注册的绘制事件
    mAttachInfo.mTreeObserver.dispatchOnDraw();
    boolean useAsyncReport = false;
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
        if (isHardwareEnabled()) {
            //调用 Window 对应 View 的 invalidate 方法
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
        } else {
            // 绘制 View
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                    scalingRequired, dirty, surfaceInsets)) {
                return false;
            }
        }
    }
    if (animating) {
        mFullRedrawNeeded = true;
        scheduleTraversals();
    }
    return useAsyncReport;
}
void invalidate() {
    mDirty.set(0, 0, mWidth, mHeight);
    if (!mWillDrawSoon) {
        scheduleTraversals();
    }
}


private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    // Draw with software renderer.
    final Canvas canvas;
    try {
        canvas = mSurface.lockCanvas(dirty);
        // TODO: Do this in native
        canvas.setDensity(mDensity);
    } 
    try {
        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
            Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                    + canvas.getWidth() + ", h=" + canvas.getHeight());
            //canvas.drawARGB(255, 255, 0, 0);
        }
  // View 绘制
        mView.draw(canvas);
        drawAccessibilityFocusedDrawableIfNeeded(canvas);
    } finally {
    }
    return true;
}


相关文章
|
5月前
|
消息中间件 前端开发 Android开发
Android面试题自定义View之Window、ViewRootImpl和View的三大流程
Android开发中,View的三大核心流程包括measure(测量)、layout(布局)和draw(绘制)。MeasureSpec类在测量过程中起到关键作用,它结合尺寸大小和模式(EXACTLY、AT_MOST、UNSPECIFIED)来指定View应如何测量。onMeasure方法用于自定义View的测量,布局阶段,ViewGroup调用onLayout确定子元素位置,而draw阶段按照特定顺序绘制背景、内容、子元素和装饰。整个流程始于ViewRootImpl的performTraversals,该方法触发测量、布局和绘制。
123 0
|
Android开发
android源码宇宙-ViewRootImpl
android源码宇宙-ViewRootImpl
263 0
android源码宇宙-ViewRootImpl
|
1月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
27 1
|
21天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
46 19
|
1月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
21天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
49 14
|
24天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
22天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
32 5