安卓页面绘制流程(2)Window和DecorView的创建

前言:

本文属于安卓页面绘制流程的第2篇,主要介绍Window和DecorView等涉及到页面展示的关键对象是如何创建的,这些主要发生在create Activity的流程中。

一.基本概念介绍

开始流程介绍之前,我们先介绍一些比较重要的角色:

1.DecorView:这是最顶层的View,继承自ViewGroup。我们经常设置的setContentView等等,都是放到DecorView中的。

2.ViewRootImpl:最顶层的ViewParent,但是并不是一个View。它属于一个管理者,维护DecorView和Window的关系。绘制流程的控制等等,都是由其来维护的。

3.PhoneWindow:Window翻译过来是窗口,它在安卓中的概念就是锁定屏幕上的一块区域进行显示。而PhoneWindow则是用来装载和显示DecorView的,我们activity中setConentView方法最终也会交给PhoneWindow的setConentView来实现。

4.WindowManagerImpl:WindowManagerGlobal的代理类,基本上功能都是交由WindowManagerGlobal处理。

5.WindowManagerGlobal:视图的管理装载类。一个应用中会有很多activity,其实每个activity都会对应一个DecorView,而这些DecorView都会保存在WindowManagerGlobal中。

6.IWindowSession:WindowManageService在客户端的Binder代理类。客户端最终的绘制操作,需要通过binder传递给WindowManageService,然后由其在传递给SurfaceFlinger去完成最终的合成。

几者的总体关系是这样的:

 

二.Activity创建流程介绍

在Activity的启动流程中,会分别执行ActivityThread的performLaunchActivity和handleResumeActivity的方法。

其中本文要介绍的Window和DecoreView的创建都发生在performLaunchActivity方法中,所以,我们来看一下这个方法:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    //1.Activity的创建
    Activity activity = null;
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    //2.Activity的关联
    activity.attach();
    //3.执行Activity的onCreate流程
    mInstrumentation.callActivityOnCreate()
    ...
}

首先,会通过反射生成Activity对象;

然后,会执行Activity的attach流程,会把对象关联到系统侧,并且创建Window对象,Window就是页面的容器。这一块,我们第二章来讲。

最后,绘制行Activity的onCreate方法,这时候,我们往往会执行setContentView方法,这时候,就会创建DecorView,ContentParent等元素。

各个元素的结构如下图所示,其中ContentParent就是所有View的父容器,我们调用setContentView()方法,也是往ContentParent中添加View。

 

三.Window创建流程

在Activity的启动流程中,会分别执行ActivityThread的performLaunchActivity和handleResumeActivity的方法。本文主要讲解的是performLaunchActivity方法中的几个关键点。

我们首先看一下ActivityThread.performLaunchActivity中的代码:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    //Activity的创建
    Activity activity = null;
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    //Activity的关联
    activity.attach();
    //执行Activity的onCreate流程
    mInstrumentation.callActivityOnCreate()
    ...
}

我们看一下activity.attach中实现的相关内容:

//android.app.Activity
final void attach(){
    //1
    mWindow = new PhoneWindow();
    //3
    mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),...);
    mWindow.setColorMode(info.colorMode);
}


//com.android.internal.policy.PhoneWindow.java
public PhoneWindow(@UiContext Context context) {
    public PhoneWindow(@UiContext Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }
}

主要执行了以下的逻辑:

1.创建了Activity所绑定的Window,成员名为mWindow,类型为PhoneWindow。

2.在PhoneWindow中,mLayoutInflater赋值。我们的布局就是通过mLayoutInflater对象去解析的。

3.给Window对象绑定WindowManager,这个WindowManager实际上是WindowManagerImpl。

所以此时,Activity中的mWindow,以及PhoneWindow中的mWindowManager和mLayoutInflater都已经有值了。

四.DecorView创建

DecorView创建

接下来,我们看下callActivityOnCreate的流程。Activity.onCreate流程中并没有和window相关的逻辑,但是正常流程,我们都会在onCreate中调用setContentView设置布局,答案就是在这个方法中,我们一起看一下:

//android.app.Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    //1
    getWindow().setContentView(layoutResID);
}

//com.android.internal.policy.PhoneWindow
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        //2
        installDecor();
    }
    ...
    //3
    mLayoutInflater.inflate(layoutResID, mContentParent);
}

private void installDecor() {
    if(mDecor == null){
        mDecor = generateDecor(-1);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    }
}

主要执行了以下的逻辑:

1.调用Activity的setContentView方法,其实就是调用其所持有window的setContentView方法。而这个window对象自然是上面流程中创建的PhoneWindow。

2.首次调用的时候,通过installDecor方法去创建根布局DecorView。而mContentParent其实是mDecor中的一个子View。

3.通过mLayoutInflater对象去解析生成布局对象,并且关联到父容器mContentParent上。我们具体讲一下这里的generateLayout方法。

ContentParent生成

我们看一下generateLayout这个方法:

//PhoneWindow.java
protected ViewGroup generateLayout(DecorView decor) {
    ...
    int layoutResource;
    if(...){
        layoutResource = R.layout.screen_title_icons;
    } else if(){
        layoutResource = R.layout.screen_custom_title;
    } else {
        layoutResource = R.layout.screen_simple;
    }
    ...
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    return contentParent;
}

//Window.java
public <T extends View> T findViewById(@IdRes int id) {
    return getDecorView().findViewById(id);
}

//DecorView.java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    final View root = inflater.inflate(layoutResource, null);
    if (mDecorCaptionView != null) {
        if (mDecorCaptionView.getParent() == null) {
            addView(mDecorCaptionView,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    } else {
        // Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    mContentRoot = (ViewGroup) root;
    initializeElevation();
}


五.总结

我们做一下总结:

首先,在attch中,创建了Window类型的对象PhoneWindow,

然后,onCreate中我们一般会调用setContentView方法,这时候会生成加载xml生成DecorView,而contentParent就是DecorView中的View。

整个流程如下图所示:

 

至此,window中的元素都已经准备好了,但是这时候只是APP侧准备好了,还没有和系统建立通讯。所以,接下来要做的就是把这个Window向系统侧进行注册,这一块就是我们下一章要将的内容了。

六.扩展性问题

1.如果onCreate中不调用setContentView,那么会执行后面的流程吗?

答:会的,即使不调用setContentView,还是会创建DecorView,而DecorView中仍然会有ContentView的父容器。只不过此时的父容器没有任何子View,显示为一片空白而已。

2.如果我在ActivityB中,想拿到上一个页面ActivityA的View该怎么做呢?

答:取WindowManagerGlobal中的mViews。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失落夏天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值