Android View的绘制流程(三)-Activity视图-WindowManager

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战

上一篇,介绍了Activity视图绘制过程中DecorView的创建,但是DecorView创建完之后不代表就已经绘制好了视图,Decorview是activity窗口的根视图,它不是最终呈现在用户面前的视图,最终呈现在用户面前的是窗口,是Window,所以创建完DecorView就需要把DecorView传递或者说给到窗口,也就是Window来显示给用户,那么怎么传递给Window呢,就是我们接下来介绍的WindowMangaer,Window的管理者。

Window

Window是android中的窗口,表示顶级窗口的意思,也就是主窗口,它有两个实现类,PhoneWindow和MidWindow,我们一般的activity对应的主要是PhoneWindow,在activity中经常使用的setContentView等方法也是在这个里面实现的。

 @Override

    public void setContentView(View view,ViewGroup.LayoutParams params) {undefined

        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);
        final Callback cb = getCallback();
        if (cb != null) {undefined
           cb.onContentChanged();  //窗口类容发生变化时更新
        }
    }

复制代码

每个主窗口中都有一个View,称之为DecorView,是主窗口中的顶级view(实际上就是ViewGroup),在View中有两个成员变量叫做mParent、mChildren,它是用来管理view的上下级关系的。而ViewGroup是对一组View的管理。因此,在ViewGroup中建立了所有view的关系网。而最终ViewGroup附属在主窗口上。这样就很容易在窗口中通过findViewById找到具体的View了。view中的事件处理也是根据这个路径来处理的。

WindowManager

WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。通过Context.getSystemService(Context.WINDOW_SERVICE)的方式可以获得WindowManager的实例.WindowManager继承自ViewManager,里面涉及到窗口管理的三个重要方法,分别是:

 * addView(); 

 * updateViewLayout();

 * removeView();  
复制代码

在WindowManager中还有一个重要的静态类LayoutParams.通过它可以设置和获得当前窗口的一些属性。 我们先来看看addView()方法,在addView中,会利用LayoutParams获得window的View属性,并为每个window创建ViewRoot,ViewRoot是View和WindowManager之间的桥梁,真正把View传递给WindowManager的是通过ViewRoot的setView()方法,ViewRoot实现了View和WindowManager之间的消息传递。在将主窗口添加到WindowManger时,它首先会建立一个代理对象:

 wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE)
复制代码

并且打开会话(IWindowSession),之后Window将通过该会话与WindowManager建立联系

看下 ActivityThread 中的 handleResumeActivity 方法:

// ActivityThread
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...... 
        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

        // If the window hasn't yet been added to the window manager,
        // and this guy didn't finish itself or start another activity,
        // then go ahead and add the window.
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        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;
            l.softInputMode |= forwardBit;
            ......
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

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

        // Get rid of anything left hanging around.
        cleanUpPendingRemoveWindows(r, false /* force */);

        // The window is now visible if it has been added, we are not
        // simply finishing, and we are not starting another activity.
        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            if (r.newConfig != null) {
                performConfigurationChangedForActivity(r, r.newConfig);
                if (DEBUG_CONFIGURATION) {
                    Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
                            + r.activity.mCurrentConfig);
                }
                r.newConfig = null;
            }
            if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
            WindowManager.LayoutParams l = r.window.getAttributes();
            if ((l.softInputMode
                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                    != forwardBit) {
                l.softInputMode = (l.softInputMode
                        & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                        | forwardBit;
                if (r.activity.mVisibleFromClient) {
                    ViewManager wm = a.getWindowManager();
                    View decor = r.window.getDecorView();
                    wm.updateViewLayout(decor, l);
                }
            }

            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
          // 这里也会调用addview
                r.activity.makeVisible();
            }
        }

        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }
复制代码

以上代码是Activity创建过程中的源码,主要做了哪些事情呢:
第一:获取DecorView,并且将DecorView通过addView方法添加到WindowManager当中;
第二:在用户触发了界面的刷新机制,就会调用WindowManager的updateViewLayout方法来更新布局;
第三:调用activity 的  makeVisible ,让视图可见,如果此时 DecorView 没有添加到 WindowManager,则把DecorView添加到WindowManager中

再看下AddView方法都做了什么:

// WindowManagerGlobal  
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
           // ......
    
            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) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            } 
}
复制代码

addView方法实例化了一个ViewRootImpl,同时调用setView方法来持有DecorView的引用,并且保存了 DecorView ,Params,以及 ViewRootImpl 的实例。

通过handleResumeActivity这个方法,可以很清楚的了解了一件事,那就是为啥 View 是在 OnResume 的时候可见的呢。

实际上,View 的绘制是由 ViewRootImpl 来负责的。每个应用程序窗口的 DecorView 都有一个与之关联的 ViewRootImpl 对象,这种关联关系是由 WindowManager 来维护的。

那么下一篇文章,我们着重看下ViewRootImpl,它是怎么负责View 的绘制的

猜你喜欢

转载自juejin.im/post/7061518682026934279