Activity的绘制流程(一)

上一篇讲了Activity的启动流程(https://www.jianshu.com/p/5e91681a8f65),从本篇开始将笔墨着重在Activity的的绘制流程上。绘制的内容将分为两大块:应用端的绘制和WMS端窗口的管理,介绍的路线如下,本篇主讲(一)窗口的添加。
(一)窗口的添加
(二)Choreographer
(三)VSync
(四)Surface
(五)RenderThread
(六)StartingWIndow
(七)窗口切换

一、窗口的管理方式

在介绍Activity的启动流程时贴了一张Activity数据结构,其实在WMS中也有与AMS中非常相似的数据结构。从下图中可以看到ActivityStack与TaskStack、TaskRecord与Task、ActivityRecord与ActivityWindowToken是一个一一对应的关系。但Window的划分比Activity更细,窗口分为应用窗口、系统窗口、子窗口,而WMS中的窗口从应用端来看是View的概念。


数据结构.jpg

二、应用窗口的添加

为了不将问题复杂化,这里先从应用端添加应用窗口出发,理清楚窗口添加的逻辑,同时也与上一篇的Activity的启动流程形成一个过渡。


addView.jpg

从上图看,添加窗口的链路并不长,但是其中涉及到的类其实很多。


addView-UML图.jpg

window-UML图.jpg
  1. 在调用ActivityThread的performLaunchActivity函数时,会创建一个Activity对象,并调用Activity的attach函数初始化了Activity的一些重要属性,也就是创建PhoneWindow对象和WindowManagerImpl对象。PhoneWindow代表了应用端的Window,一个Activity对应一个Window。WindowManagerImpl用于添加、更新、删除View。
ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        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, window, r.configCallback,
                        r.assistToken);
        ...
    }
    
Activity.java
    final void attach() {
        ...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...
    }
    
SystemServiceRegistry.java
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
  1. 写Activity的时候在onCreate函数中调用setContentView函数加载布局文件,在PhoneWindow中会新建DecorView对象mDecor,若所有的View构成一个View树,DecorView代表了根View。DecorView中包含一个LinearLayout的mContentRoot,这个LinearLayout包含两个部分,标题栏和内容栏,内容栏就是PhoneWindow的ViewGroup对象mContentParent。在setContentView函数设置的布局就放在mContentParent中。
Activity.java
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
    }
    
PhoneWindow.java
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        ...
    }
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        }
        ...
    }
    protected DecorView generateDecor(int featureId) {
        ...
        return new DecorView(context, featureId, this, getAttributes());
    }
    protected ViewGroup generateLayout(DecorView decor) {
        ...
        int layoutResource;
        layoutResource = R.layout.screen_simple;
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ...
        return contentParent;
    }

DecorView.java
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        ...
        final View root = inflater.inflate(layoutResource, null);
        mContentRoot = (ViewGroup) root;
        ...
    }
screen_simple.xml
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
        <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>
  1. 在调用ActivityThread的handleResumeActivity函数时,会获取上面创建的DecorView对象。将该DecorView添加到WMS中。WindowManagerGlobal是单例的,一个进程只有一个WindowManagerGlobal对象,WindowManagerGlobal中管理着所有的View和View对应的ViewRootImpl和属性,ViewRootImpl的作用是操作View。PhoneWindow的adjustLayoutParamsForSubWindow函数会根据View的类型设置WMS中对应窗口的分组属性,这里再次强调应用端的View对应WMS中的窗口。
    <1> 在ActivityThread的handleResumeActivity函数中,指定DecorView的类型为TYPE_BASE_APPLICATION,则为应用窗口,所以WMS中窗口的分组属性为Token的代理对象。Token定义在ActivityRecord中,继承自IApplicationToken.Stub。
    <2> 若为子窗口(1000~1999),WMS中窗口的分组属性为W的代理对象。W定义在ViewRootImpl中,继承自IWindow.Stub。
    <3> 若为系统窗口(2000~2999),WMS中窗口的分组属性为null。
ActivityThread.java
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...
        final Activity a = r.activity;
        ViewManager wm = a.getWindowManager();
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        a.mDecor = decor;
        wm.addView(decor, l);
        ...
    }
    
WindowManagerImpl.java
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        ...
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    
WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }
        ViewRootImpl root;
        root = new ViewRootImpl(view.getContext(), display);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        root.setView(view, wparams, panelParentView);
        ...
    }

PhoneWindow.java
    void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
        CharSequence curTitle = wp.getTitle();
        if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            if (decor != null) {
                wp.token = decor.getWindowToken();
            }
            ...
        } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
            ...
        } else {
            if (wp.token == null) {
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
            }
        }
    }

ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ...
        requestLayout();
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
        ...
    }
  1. WMS的addWindow函数主要的作用是检查是否可添加窗口,根据窗口类型(应用窗口、子窗口、系统窗口)创建或找到WindowToken对象,若为应用窗口则有对应的AppWindowToken对象。WindowToken和AppWindowToken代表着窗口令牌。创建对应的WindowState对象,WindowState代表着窗口。应用窗口的Parent为AppWindowToken、子窗口的Parent为WindowState,系统窗口的Parent为WindowToken。WindowState会计算mBaseLayer和mSubLayer,用于保存到Parent保存的Children列表中,保存的顺序影响着显示的顺序。WMS还会根据上面设置的窗口分组属性作为键,WindowState做为值加入到WMS的mWindowMap中,mWindowMap是一个WindowHashMap,保存着所有的窗口。
子窗口的保存顺序:
    private static final Comparator<WindowState> sWindowSubLayerComparator =
        new Comparator<WindowState>() {
            public int compare(WindowState w1, WindowState w2) {
                final int layer1 = w1.mSubLayer;
                final int layer2 = w2.mSubLayer;
                if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
                    return -1;
                }
                return 1;
            };
    };
应用窗口和系统窗口的保存顺序:
    private final Comparator<WindowState> mWindowComparator =
            (WindowState newWindow, WindowState existingWindow) -> {
        return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
    };
    protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
            WindowState existingWindow) {
        return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
    }

总而言之,WMS管理着所有的窗口,窗口的位置、可见性、显示顺序等都是由WMS决定的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容