上一篇文章根据源码分析了下Android的App启动的整个流程,那么App启动起来之后,是需要和用户交互的,所以,界面的显示内容是极其重要的一部分。然而大家都知道,Android的UI基本上都是附属在Acticity
上的,那么接下来就分析一下,Activity启动后,视图是如何加载出来的。
Window
在开始分析视图创建之前,首先让我们先了解一下Window
。相信大家之前应该遇见过这个类。但是实际开发中却很少能直接接触到Window
。其实我们可以顾名思义,可以把Window
看成是Activity
或者Toast
和Dialog
的窗口。就是这写组件需要展示的内容,都会附属到Window
上。然而,Window
是一个抽象类,其具体的实现类是PhoneWindow
。那么下面我们要分析Activity
的视图创建,很显然首先需要创建Window
,比较Activity
的视图是需要附属到Window
上的。
Window的创建
Window
是在哪里被创建的呢?还记得 Android 6.0 View加载流程源码分析 中讲到的performLaunchActivity
这个方法吗,没错就是从这里开始的。在这里面,当Activity
被创建之后,会调用其attach
方法。其里面有如下一段代码:
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this);
首先直接new 了一个Window
的实现类PhoneWindow
对象,然后为其设置Callback
接口回调。因为Activity
实现了Window
的Callback
和OnWindowDismissedCallback
接口,所以这里都直接传入了当前的Activity
。这个Callback
里面有一系列事件的回调方法,这里面有我们熟悉dispatchTouchEvent
,dispatchKeyEvent
等等的方法。到此,Avtivity
中的 Window
对象算是创建成功了。
既然Window
已经创建了,那么我们的View如何附属到Window
上呢?其实是通过WindowManager
的addView
方法。但是这里我们的视图还没创建呢,就先分析完视图的创建吧。
视图创建
对于视图的创建又该从何开始呢?这个方法其实每一个Android开发者都再熟悉不过了。setContentView
!嗯,就是从这里开始,下面是这个方法的源码。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
看到了吧,这里又调用了之前在attach
方法中创建好的Window
对象的setContentView
方法。进去看看。
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
当然这个是PhoneWindow
里面的方法,原因上面已经说到了,Activity
里面创建的就是PhoneWindow
的对象。然后我们看代码。一进去首先判断mContentParent
是否为空。这个mContentParent
其实是DecorView
里面的内容区域其实就是一个FrameLayout
相信大家了解这方面的只是,不了解的可以自行谷歌。网上有很多说明setContentView
为何不叫setView
的文章。嗯,其实这里的mContentParent
肯定为空的,如果是第一次加载视图的话。那么就会进入installDecor
方法。这个方法完成了两件事,调用generateDecor
方法创建了DecorView
。然后在generateLayout
方法里面创建了上面说到 的mContentParent
。
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in