美文网首页
ViewRootImpl细节问题(平常代码根本不会写)

ViewRootImpl细节问题(平常代码根本不会写)

作者: 王多鱼2 | 来源:发表于2020-11-29 22:58 被阅读0次

    个人感觉,这些东西了解就行,都会了,干脆些源码去算了;
    里面有很多就是抄拷贝别人的

    1,首次 View 的绘制流程是在什么时候触发的?

    答案是在 ActivityThread.handleResumeActivity 里触发的。

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

    ActivityThread.handleResumeActivity 里会调用 wm.addView 来添加 DecorView,wm 是 WindowManagerImpl

    // WindowManagerImpl
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    // WindowManagerGlobal
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // 这里的 view 就是 DecorView
        // ...
        ViewRootImpl root;
        View panelParentView = null;
    
        synchronized (mLock) {
            // ...
            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);
            } catch (RuntimeException e) {
            }
        }
    }
    // ViewRootImpl.setView
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      requestLayout();
    }
    

    最终通过 WindowManagerImpl.addView -> WindowManagerGlobal.addView -> ViewRootImpl.setView -> ViewRootImpl.requestLayout 就触发了第一次 View 的绘制。

    ViewRootImpl 创建的时机?

    从上面流程里可以看到,ViewRootImpl 也是在 ActivityThread.handleResumeActivity 里创建的。

    ViewRootImpl 和 DecorView 的关系是什么?

    // ViewRootImpl.setView
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      requestLayout();
      // ...
      // 这里的 view 是 DecorView
      view.assignParent(this);
    }
    

    接着上面的代码看,在 ViewRootImpl.setView 里,通过 DecorView.assignParent 把 ViewRootImpl 设置为 DecorView 的 parent。

    所以 ViewRootImpl 和 DecorView 的关系就是 ViewRootImpl 是 DecorView 的 parent。
    因为 DecorView 是我们布局的顶层,现在我们就知道层层调用 requestLayout 等方法是怎么调用到 ViewRootImpl 里的了。

    Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?

    其实上面的问题中,我们经常会说到 PhoneWindow 这个角色,PhoneWindow 其实是 Window 的唯一子类,是 Activity 和 View 交互系统的中间层,而 DecorView 是整个 View 层级的最顶层,ViewRootImpl 是 DecorView 的 parent,但是他并不是一个真正的 View,只是继承了 ViewParent 接口,用来掌管 View 的各种事件,包括 requestLayout、invalidate、dispatchInputEvent 等等。

    如何触发重新绘制?

    既然上面说到 View 的绘制流程,那我们怎么触发 View 的重新绘制呢?
    就是调用 requestLayout 和 invalidate。

    requestLayout 和 invalidate 的流程

    requestLayout 和 invalidate 的区别

    requestLayout 只会触发 measure 和 layout,invalidate 只会触发 draw

    相关文章

      网友评论

          本文标题:ViewRootImpl细节问题(平常代码根本不会写)

          本文链接:https://www.haomeiwen.com/subject/meslwktx.html