Window、DecorView、ViewRootImp详解

35 篇文章 1 订阅

在这里插入图片描述

Window和DecorView的创建

先从比较熟悉的activity.setContentView说起。

Activity.java

private Window mWindow;

	......

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

	......

public Window getWindow() {
	return mWindow;
}

可以发现,Activity的setContentView()实际上调用的是Window的setContentView(),再来看下mWindow的初始化:

Activity.java

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) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(mWindowControllerCallback);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
		
	......
	
    if (voiceInteractor != null) {
        if (lastNonConfigurationInstances != null) {
            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        } else {
            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                    Looper.myLooper());
        }
    }

    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();
    mCurrentConfig = config;

    mWindow.setColorMode(info.colorMode);
    mWindow.setPreferMinimalPostProcessing(
            (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);

    setAutofillOptions(application.getAutofillOptions());
    setContentCaptureOptions(application.getContentCaptureOptions());
}

可以看到mWindow是在Activity中的attach()方法被创建的,实际上是一个PhoneWindow对象,PhoneWindow是Android中Window的唯一实现类。系统会为每个Activity创建一个与之对应的Window。

创建Window后,还会通过Context.getSystemService()的方式获取WindowManager的实例,然后设置给mWindow。WindowManager是一个窗口管理类。

下面来看PhoneWindow的setContentView()

PhoneWindow.java

@Override
public void setContentView(int layoutResID) {
	//先判断mContentParent是否初始化
    if (mContentParent == null) {
        installDecor();
    //如果Activity没有过度动画,多次调用setContent会移除mContentParent内部	View
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    	//设置动画场景
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
    	//将资源文件通过LayoutInflater对象转换成View树
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

重点关注installDecor()

PhoneWindow.java

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    
    ......

installDecor()主要干两件事情

  1. 如果Decor没有初始化,则同个generateDecor()初始化
  2. 如果mContentParent没有初始化,则通过generateLayout()初始化

generateDecor很简单就是new一个DecorView实例

protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, this);
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

下面来分析 generateLayout()

protected ViewGroup generateLayout(DecorView decor) {
    // 从主题文件中获取样式信息
    TypedArray a = getWindowStyle();

    ...................
	//一堆if判断,根据设置的主题样式来设置DecorView的风格
    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestFeature(FEATURE_ACTION_BAR);
    }

    ................

    // 根据主题样式,加载窗口布局
    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
    } else if(...){
        ...
    }

    View in = mLayoutInflater.inflate(layoutResource, null);//加载layoutResource

    //往DecorView中添加子View,即文章开头介绍DecorView时提到的布局格式,那只是一个例子,根据主题样式不同,加载不同的布局。
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 
    mContentRoot = (ViewGroup) in;

    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// 这里获取的就是mContentParent
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

    if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
        ProgressBar progress = getCircularProgressBar(false);
        if (progress != null) {
            progress.setIndeterminate(true);
        }
    }

    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        registerSwipeCallbacks();
    }

    // Remaining setup -- of background and title -- that only applies
    // to top-level windows.
    ...

    return contentParent;
}

generateLayout()主要是先从主题中获取样式,然后根据样式,加载对应的布局到DecorView中,然后获取mContentParent。然后为mContentParent添加Activity中的布局。

小结

  1. 一个Acitivty对应一个Window,Window是在activity.attach()的时候被创建的
  2. 在Activity中调用setContentView()实际上是调用了PhoneWindow的setContentView()
  3. 调用setContentView()时,会去初始化DecorView以及其内部的TittleView和mContentParent,可以通过设置Window.FEATURE_NO_TITTLE使得DecorView内部只存在mContentParent。

通过WinowManager管理Window

上面主要讲的是Window的创建、DecorView的创建以及DecorView的子View是如何被添加到DecorView中的。每个Activity都有一个与之关联的Window,那么如何管理Window呢,这是就要借助WindowManager类了。WindowManager管理Window实际上就是在管理Window中的DecorView。下面来看一下DecorView和WindowManager是如何关联起来的。

从Activity的启动开始,具体可以参考: Activity的启动流程详解

创建流程最后一步为AMS通过ApplicationThread用Handler H发送了一个message,最后执行了ActivityThread的handleLaunchActivity方法。

ActivityThread.handleLanuchActivity() ——>ActivityThread.performLanuchActivity()——>
Activity.attach()——>Activity.onCreate()——>ActivityThread.handleResumeActivity()——>
ActivityThread.performResumeActivity()——>Activity.onResume()——>WindowManager.addView()

final void handleResumeActivity(IBinder token, boolean clearHide, 
                                boolean isForward, boolean reallyResume) {

    //这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
    ActivityClientRecord r = performResumeActivity(token, clearHide);

    if (r != null) {
        final Activity a = r.activity;
        if (r.window == null && !a.mFinished && willBeVisible) {
       		//获得Window对象
            r.window = r.activity.getWindow();
            //获得Window中的decorview
            View decor = r.window.getDecorView();
            //decor对用户不可见
            decor.setVisibility(View.INVISIBLE);
            //获得在Activity.attach()中创建的WindowManager
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;

            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                //将DecorView添加进WindowManager,但是这个时候,还是不可见的
                wm.addView(decor, l);
            }

            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                //在这里,执行了重要的操作,使得DecorView可见
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }
        }
    }
}

在resume过程中,通过调用windowManager.addView(decor,lp)将WindowManager和DecorView建立关联。
WindowManager继承了ViewManager接口,但实际上其本身依然是一个接口,实现类是WindowManagerImpl

ViewManager.java

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

WindowManagerImpl.java

public final class WindowManagerImpl implements WindowManager {    
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @VisibleForTesting
    public final Context mContext;
    private final Window mParentWindow;
    ...
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    
    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
    
    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }
}

同样实现了ViewManager接口的还有ViewGroup,我们知道ViewGroup也有addView方法,但是在ViewGroup中是将普通的View或者ViewGroup作为Children加入,而在WindowManagerImpl是将DecorView作为根布局加入到PhoneWindow中。

WindowManagerImpl并没有直接实现操作View的相关方法,而是全部交给WindowManagerGlobal。WindowManagerGlobal是一个单例类。

public static WindowManagerGlobal getInstance() {
    synchronized (WindowManagerGlobal.class) {
        if (sDefaultWindowManager == null) {
            sDefaultWindowManager = new WindowManagerGlobal();
        }
        return sDefaultWindowManager;
    }
}

在这里插入图片描述

深入分析WindowManagerGlobal

分析WindowManagerGlobal实际上就是分析其内部的三个方法:addView/updateView/removeView

这里重点分析addview的实现

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId)

该方法的主要作用是将decorView添加到ViewRootImpl中,通过viewRootImpl对其内部的view进行管理

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {

		//参数检查
        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;
        //判断是否有父window,从而调整当前窗口layoutParams
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
            	//创建一个Runnable,用来遍历更新所有ViewRootImpl,更新系统参数
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                //添加到执行队列中
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
			
			//看需要add的view是否已经在mViews中,WindowManager不允许同一个View被添加两次
            int index = findViewLocked(view, false);
            if (index >= 0) {
            	//如果被添加过,就看是否在死亡队列中也存在
                if (mDyingViews.contains(view)) {
                    //如果当前view存在于死亡队列,先将这个已经存在的view对应的window移除
                    mRoots.get(index).doDie();
                } else {
                	//否则,说明View已经被添加,不需要再重新添加了
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

			//重点在这里,创建了一个ViewRootImpl对象
            root = new ViewRootImpl(view.getContext(), display);
			//给需要添加的View设置params,即decorView
            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 {
            	//将decorView设置给ViewRootImpl
            	//ViewRootImpl向WMS添加新的窗口,申请Surface以及decorView在Surface上重绘
            	//这才是真正意义上完成了窗口的添加操作
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

这里为新窗口创建了一个ViewRootImpl对象。ViewRootImpl负责于WMS进行直接的通讯,负责管理Surface,负责触发空间的测量与布局,负责触发控件的绘制,同事也是输入时间的中转站。总之,ViewRootImpl是整个控件系统正常运转的动力,是很关键的一个组件。

ViewRootImpl管理View

具体分析下ViewRootImpl中setView流程

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
            mView = view;

            mAttachInfo.mDisplayState = mDisplay.getState();
            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);

            mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
            mFallbackEventHandler.setView(view);
            //mWindowAttributes保存了窗口所对应的LayoutParams
            mWindowAttributes.copyFrom(attrs);
            if (mWindowAttributes.packageName == null) {
                mWindowAttributes.packageName = mBasePackageName;
            }
            mWindowAttributes.privateFlags |=
                    WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;

            attrs = mWindowAttributes;
			
			......
			
			//请求UI开始绘制
			requestLayout();
			//初始化mInputChannel
			//InputChannel是窗口接受来自InputDispatcher的输入事件管道
			//注意,仅当窗口属性inputFeatures不含有INPUT_FEATURE)_NO_INPUT_CHANNEL时才会
			//创建InputChannel
            InputChannel inputChannel = null;
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                inputChannel = new InputChannel();
            }
            mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                    & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
            try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    adjustLayoutParamsForCompatibility(mWindowAttributes);
                    //通知WindowManagerService添加一个窗口,注册一个时间监听管道
                    //用来监听案件keyEvent和触摸MotionEvent时间
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
                    setFrame(mTmpFrame);
                }
                
                ......
    }

setView内部执行过程

ViewRootImpl.setView—>ViewRootImpl.requestLayout—>ViewRootlmpl.scheduleTraversals—>ViewRootImpl.doTraversal—>ViewRootImpl.performTraversals—>进入View的绘制流程

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

 @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
			//开始View的绘制
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值