DecorView如何添加到Window

这篇博客详细解析了DecorView如何添加到Window的过程,从Activity的启动、 attach方法、callActivityOnCreate,到WindowManagerImpl的addView方法,再到requestLayout方法触发的绘制流程。文章指出,DecorView的添加始于ActivityThread的handleLaunchActivity,经过一系列步骤,最终在ViewRootImpl的setView方法中,通过view.assignParent(this)设置DecorView的父视图为ViewRootImpl,从而确保UI绘制流程的正确执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前几篇文章讲了
从setContentView开始,了解view的加载过程
LayoutInflater 是怎么把xml添加到decorview?
今天来看一下

DecorView如何添加到Window

1,首先要了解Activity的启动过程

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  ···省略若干行
        Activity a = performLaunchActivity(r, customIntent); 
  ···省略若干行   
    }

Activity是通过ActivityThead启动的,首先会走handleLaunchActivity方法,之后会走 performLaunchActivity(r, customIntent)方法,如下

performLaunchActivity(xxxxxx){
Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ···省略若干行
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
              //调用attach方法
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);
                         ···省略若干行
}

看performLaunchActivity方法,在这个方法里会通过反射来拿到我们当前的Activity,然后会调用attach方法,之后再看attach方法;

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
        //在这里初始化了PhoneWindow。。
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);

再回到performLaunchActivity方法,当attch完成之后会走callActivityOnCreate方法。

performLaunchActivity
。。省略若干行
if (r.isPersistable()) {
       //斤如callActivityOnCreate方法,它会执行activity.performCreate方法     mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }

也就是说Activity的生命周期,执行顺序,调用流程都会在ActivityThread这个类中执行,例如callActivityOnRestoreInstanceState,callActivityOnPostCreate等。

我们再回到handleLaunchActivity,当它走完performLaunchActivity方法之后,会调用handleResumeActivity,callActivityOnPause等方法
如下,

{
//点进去最终会是callActivityOnResume这个方法来调用OnResume方法
handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);
            if (!r.activity.mFinished && r.startsNotResumed) {            
                try {
                    r.activity.mCalled = false;
                    mInstrumentation.callActivityOnPause(r.activity);
}
if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                //获取到DecorView
                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 (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //把decor添加到WindowManager中
                    wm.addView(decor, l);
                }

从上面可以看到当走完handleResumeActivity方法后,通过View decor = r.window.getDecorView();获取到DecorView并设置为隐藏。然后获取WindowManager,然后把decor添加到WindowManager中;
之后我们再进入WindowManager中,发现它是一个接口。通过搜索发现它的实现类为WindowManagerImpl,上面代码中的 wm.addView(decor, l) 这个方法就是走的WindowManagerImpl中的addView方法。

我们进WindowManagerImpl这个类看看者方法

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //这里其实是WindowManagerGlobal 实例的addView方法
        //这里的view就是我们传进来的DecorView
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

进入 mGlobal.addView方法

 public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
 。。。省略
 //ViewRootImpl 这个类很重要,绘制流程基本都是在这个类完成的。
        ViewRootImpl root;
        View panelParentView = null;  
       。。。省略      
       //这里new一个ViewRootImpl,绘制流程基本都是在这个类完成的
           root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
        //这是三个List,把他们添加到一个list中去,也就是把他们都保存到这个类中,这个类是管理root ,和decorView的。
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        // do this last because it fires off messages to start doing things
        try {
        //这里调用setView把我们的decorView和一些参数传进去
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
          }

//以上就是Activity的启动过程中生成phonewindow,并加载DecorView,之后发起绘制的基本过程。

进一步深入了解,我们再进入setView()方法,首先把传进去的DecorView赋值给mView,然后调用mWindowSession.addToDisplay这个方法把我们的DecorView显示出来。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
            //把传进去的DecorView赋值给mView
                mView = view;
                。。省略 者其中是一些动画的处理和输入法的处理等
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();//这个很常见
                。。省略
                 try {
                 //下面是发起一个跨进程的通信,通过方法名可以猜测,是用来显示的
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
           }         
        }
    }
    ...省略
    //关键代码 
    //DecorView把WindowManagerImpl设置为顶层ViewGroup
     view.assignParent(this);

我们再进入这个方法view.assignParent(this);这个方法是View中的方法

//就送给我们的View分配Parent
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");
        }
    }

讲一下requestLayout这个方法
首先,View.requestLayout—>其实是调用View类的mParent .requestLayout–>最终会到DecorView的requestLayout,在这个里面会调用ViewRootImpl的requestLayout。为什么会最终会调用DecorView的requestLayout,是因为在setView()方法的时候利用assignParent(ViewParent parent)设置了DecorView的根部局。所以这个mParent 其实就是ViewRootImpl。 其次,走到ViewRootImpl.requestLayout后就会重新进行绘制流程,代码如下

 @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals(); //遍历操作
        }
    }

scheduleTraversals()方法

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

这里有一个线程,看一下mTraversalRunnable,它会不断的走 doTraversal()方法

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

再看一下 doTraversal()方法,里面重要的是 performTraversals()方法,可以猜测这个就是发起重绘的方法。

void doTraversal() {
       。。省略
            performTraversals();
       。。省略
    }

继续看 performTraversals()

 private void performTraversals() {
        // 这个就是我们的DecorView
        final View host = mView;
        //...省略一些操作,大家可以通过变量名来猜测它是干什么的
        //顶层视图DecorView所需要窗口的宽度和高度,因为我们必须要知道父容器的宽高才能测量子View的宽高。 
        int desiredWindowWidth;
        int desiredWindowHeight;
        mDisplay.getRealSize(size);
        desiredWindowWidth = size.x;
        desiredWindowHeight = size.y;
        else {
        //这这里的宽高是是手机屏幕的宽高,见下面代码
                DisplayMetrics packageMetrics =
                    mView.getContext().getResources().getDisplayMetrics();
                desiredWindowWidth = packageMetrics.widthPixels;
                desiredWindowHeight = packageMetrics.heightPixels;
            }
        。。。省略,大部分为初始化操作
         // Ask host how big it wants to be
         //重点方法,通过注释可以猜测,这里是测量布局所占用空间。
   performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
 。。。省略
 //
   performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    。。。省略
//
   performDraw();

}

进入 performMeasure方法看一看,它是会调用DecorView的measure方法,DecorView是继承View的,所以实际调用的是View的measure方法。所以这些测量,layout,draw都是再ViewRootImpl发起的。

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

//以上就是Activity的启动过程中生成phonewindow,并加载DecorView,之后发起绘制的过程。

总结:DecorView如何添加到Window?
看下面绘制流程图,可以发现最终调用了ViewRootImpl.setView,在setview方法里调用了view.assignParent(this);,将Decorview的mParent设置成ViewRootImpl
这也就是为什么View调用requestLayout方法的时候最终会走到ViewRootImpl的requestLayout
这里写图片描述
这篇博客也帮大家找到了UI绘制流程的起始点是在生什么地方,也就是下面三个方法:///测量–performMeasure()// 摆放布局–performLayout()
// 绘制–performDraw()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值