Graphic Window windowmanager

2022-05-25  本文已影响0人  xiaoluo
Window、WindowManager和WMS的关系

View,Window是抽象概念,却是Android中视图的呈现方式。View不能单独存在,需附着在Window抽象上。Activity、Dialog、Toast,PopUpWindow都是视图,都会对应着一个Window。

Window创建

PW和WM关联

Activity 启动过程中会调用 ActivityThread 的 performLaunchActivity 方法,该方法内部调用Activity的attach方法

Activity::attach

final void attach(....) {
       ....
        // 1 
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
       ....
        // 2 
        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());
        }
        ....

1 创建Window抽象的实例 PhoneWindow。
2 getSystemService 方法得到的是WindowManagerImpl实例。

Window::setWindowManager

    public void setWindowManager(....) {
     //返回WindowManagerImpl
       ....
        //   1
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

1 这次创建WMI时将创建它的 PW 作为参数传了进来,WMI就持有了 PW的引用,可以对其进行操作。上面提到过view不能单独存在需要添加到Window。

WMI的addView操作

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

1 调用了 WindowManagerGlobal 的 addView 方法,其中最后一个参数mParentWindow 就是上面提到的 PhoneWindow,WMI虽是WM 的实现,但却将功能实现委托给了WMG。

创建View视图

如何将Activity对应的View添加到Window呢?入口在onCreate的setContentView处。

Activity ::setContentView

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

最终辗转调用到PhoneWindow::setConentView()

    public void setContentView(int layoutResID) {
....
           // 1  
            installDecor();
....
       // 2
        mLayoutInflater.inflate(layoutResID, mContentParent);
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

1 DecorView的创建由installDecor完成,通过generateDecor创建DecorView,这个时候DecorView还只是一个空白的FrameLayout。

2 将Activity的视图添加到DecorView的mContentParent中

上述过程全部完成,Activity的布局文件也已经成功添加到了DecorView的mContentParent中,但是这个时候DecorView还没有被WindowManager正式添加到Window中。

将View添加到VM

在ActivityThread的handleResumeActivity方法中,首先会调用Activity的onResume方法,接着会调用Activity的makeVisible(),正是在makeVisible方法中,DecorView真正地完成了添加和显示这两个过程 。

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
           // 1
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

到这里,Activity的View视图被添加到了Window中。

1 调用WMG的addview方法重绘视图

视图重绘

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
      ....
        if (parentWindow != null) {
            //  1
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        ....
        ViewRootImpl root;
        View panelParentView = null;
         ....
           //  2 
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
           // 3 
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
           // 4
            root.setView(view, wparams, panelParentView);
         ....
    }
   //在窗口的添加、更新和删除过程中都会涉及这3个列表
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

1 如果当前窗口要作为子窗口,就会根据父窗口对子窗口的WindowManager.LayoutParams 类型的 wparams 对象进行相应调整。
2 创建了ViewRootImp并赋值给root
3 将添加的View保存到View列表中
4 处将窗口和窗口的参数通过setView方法设置到ViewRootImpl中,可见我们添加窗口这一操作是通过ViewRootImpl来进行的。ViewRootImpl身负了很多职责,主要有以下几点:

Window的更新

WMI的updateViewLayout方法会调用WMG的updateViewLayout方法

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        view.setLayoutParams(wparams);
           ....
            mParams.add(index, wparams);
            //  1
            root.setLayoutParams(wparams, false);
    }

ViewRootImpl 的 setLayoutParams 方 法 将 更 新 的 参 数 设 置 到 ViewRootImpl 中。ViewRootImpl 的 setLayoutParams 方法在最后会 调用ViewRootImpl的scheduleTraversals方法

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

scheduleTraversals 会触发三大流程

参考资料:android 进阶解密 android开发艺术探索

上一篇下一篇

猜你喜欢

热点阅读