Activity、View、Window的理解

简单的来说:Activity的内部实际上持有了一个Window的子类PhoneWindow。Activity中关于界面的绘制实际上是交给PhoneWindow中的setContentView方法来实现。

Activity在onCreate之前调用attach方法,在attach方法中会创建window对象。window对象创建时并没有创建Decor对象对象。用户在Activity中调用setContentView,然后调用window的setContentView,这时会检查DecorView是否存在,如果不存在则创建DecorView对象,然后把用户自己的View 添加到DecorView中。


下面将深入解释上面的内容

Activity被创建后,系统会调用它的attach方法来将Activity添加到ActivityThread当中。我们找到Activity的attach方法如下:

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,
            Object lastNonConfigurationInstance,
            HashMap<String,Object> lastNonConfigurationChildInstances,
            Configuration config) {
    attachBaseContext(context);
    mWindow= PolicyManager.makeNewWindow(this);
    mWindow.setCallback(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    mUiThread = Thread.currentThread();
    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstance = lastNonConfigurationInstance;
    mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
    mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
}

PolicyManager.makeNewWindow(this)就是创建Window对象的代码。其内部实现调用了Policy对象的makeNewWindow方法,其方法直接new了一个PhoneWindow对象如下:

public PhoneWindow makeNewWindow(Context context) {
        return new PhoneWindow(context);
}

这时我们已经可以把流程串起来,Activity创建后系统会调用其attach方法,将其添加到ActivityThread当中,在attach方法中创建了一个window对象。

下面分析View的创建。我们知道Window聚合了DocerView,当用户调用setContentView的时候会把一颗View树仍给DocerView.View树是已经创建好的实例对象了,所以我们研究的是DocerView是个什么东西,它是如何被创建的。 我们先看看Window实现里边的setContentView方法,下面是PhoneWindow中的setContentView方法的实现:

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    if (mContentParent == null) {
        installDecor();
    } else {
        mContentParent.removeAllViews();
    }
    mContentParent.addView(view, params);
    final Callback cb = getCallback();
    if (cb != null) {
        cb.onContentChanged();
    }
}

Window内部首先判断mContentParent是否为空,然后调用installDecor方法(安装装饰器),我们看看这个方法如何实现的:

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();
        mDecor.setIsRootNamespace(true);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
        if (mTitleView != null) {
            if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                View titleContainer = findViewById(com.android.internal.R.id.title_container);
                if (titleContainer != null) {
                    titleContainer.setVisibility(View.GONE);
                } else {
                    mTitleView.setVisibility(View.GONE);
                }
                if (mContentParent instanceof FrameLayout) {
                    ((FrameLayout)mContentParent).setForeground(null);
                }
            } else {
                mTitleView.setText(mTitle);
            }
        }
    }
}

在该方法中,首先创建一个DecorView,DecorView是一个扩张FrameLayout的类,是所有窗口的根View。我们在Activity中调用的setConctentView就是放到DecorView中了。这是我们类图的聚合关系如下:

Activity--->Window--->DecorView

这是我们得出这3个类之间最直接的一个关系。 我们看上面代码的红色部分setContentView-> installDecor-> generateDecor. generateDecor直接new了一个DecorView对象:

protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
}

我们可以去看看DecorView的实现,它是PhoneWindow的一个内部类。实现很简单,它默认会包含一个灰色的标题栏,然后在标题栏下边会包含一个空白区域用来当用户调用setContentView的时候放置用户View,并传递事件,这里不做详细分析,感兴趣同学可以自己研究研究。 当DecorView创建好之后再回到Window中的setContentView方法中来,见上面代码蓝色部分,调用 mContentParent.addView(view, params); 来将用户的View树添加到DecorView中。

posted @ 2017-09-14 18:16  天涯海角路  阅读(209)  评论(0)    收藏  举报