8.3 Window的创建过程
View
是Android
中的视图的呈现方式,但是View
不能单独存在,它必须附着在Window
这个抽象的概念上面,因此有视图的地方就有Window
。Android
中可以提供视图的地方有Activity
,Dialog
,Toast
,除此之外,还有一些依托Window
而实现的视图,比如PopUpWindow
,菜单,它们也是视图,有视图的地方就有Window
,因此Activity
,Dialog
,Toast
等视图都对应着一个Window
,本节将分析这些视图元素中的Window
的创建过程。
8.3.1 Activity的Window创建过程
要分析Activity
中的Window
的创建过程就必须了解Activity
的启动过程,Activity
的启动过程很复杂,最终会由ActivityThread
中的performLaunchActivity()
来完成整个启动过程,在这个方法内部会通过类加载器创建Activity
的实例对象,并调用其attach
方法为其关联运行过程中所依赖的一系列上下文环境变量。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
. . .
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
// 通过类加载器创建 Activity 的实例对象
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
. . .
} catch (Exception e) { . . . }
try {
. . .
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, ". . . ");
// 调用其 attach 方法为其关联运行过程中所依赖的一系列上下文环境变量
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);
. . .
}
} catch (Exception e) { . . . }
return activity;
}
在Activity
的attach()
方法里,系统会创建Activity
所属的Window
对象并为其设置回调接口,Window
对象的创建是通过PolicyManager
的makeNewWindow
方法实现的。由于Activity
实现了Window
的Callback
接口,因此当Window
接收到外界的状态改变时就会回调Activity
的方法。Callback
接口中的方法很多,但是有几个却是我们都非常熟悉的,比如onAttachedToWindow
,onDetachedFromWindow
,diapatchTouchEvent
,等等
// 创建 Window 对象
mWindow = PolicyManager.makeNewWindow(this);
// 实现 Window 的 Callback 接口
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
. . .
从上面的分析可以看出,Activity
的Window
是通过PolicyManager
的一个工厂方法来创建的,但是从PolicyManager
的类名可以看出,它不是一个普通的类,它是一个策略类。PolicyManager
中实现的几个工厂方法全部在策略接口IPolicy
中声明了。
public interface IPolicy {
public Window makeNewWindow(Context context);
public LayoutInflater makeNewLayoutInflater(Context context);
public WindowManagerPolicy makeNewWindowManager();
public FallbackEventHandler makeNewFallbackEventHandler(Context context);
}
在实际的调用中,PolicyManager
的真正实现是Policy
类,Policy
类中的makeNewWindow
方法的实现如下,由此可以发现,Window
的具体实现的确是PhoneWindow
。
public static Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
关于策略类PolicyManager
是如何关联到Policy
上面的,这个无法从源码中的调用关系来得出,这里猜测可能是有编译环节动态控制的。到这里Window
已经创建完成了,下面分析Activity
的视图是怎么附属在Window
上的。由于Activity
的视图由setContentView
方法提供,我们只需要看setContentView
方法的实现即可:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
从Activity
的setContentView
的实现可以看出,Activity
将具体实现交给了Window
处理,而Window
的具体实现是PhoneWindow
,所以只需要看PhoneWindow的相关逻辑即可。
PhoneWindow
的setContentView
方法大致遵循如下几个步骤:
1. 如果没有DecorView,那么就创建它
DecorView
是一个FrameLayout
,DecorView
是Activity
中的顶级View
,一般来说,它的内部包含标题栏和内部栏,但是这个会随着主题的变换而发生改变。不管怎么样,内容栏是一定要存在的,并且内容栏具体固定的id
,那就是content
,它的完整id
是android.R.id.content
。DecorView
的创建过程由installDecor
方法来完成,在方法内部会通过generateDecor
方法来直接创建DecorView,这个时候DecorView
还只是一个空白的FrameLayout
。
protected DecorView generateDecor(){
return new DecorView(getContext(),-1);
}
为了初始化DecorView
的结构,PhoneWindow
还需要通过generateLayout
方法来加载具体的布局文件到DecorView
中,具体的布局文件和系统版本以及主题有关。
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
其中ID_ANDROID_CONTENT
的定义如下,这个id
所对应的ViewGroup
就是mContentParent:
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
2. 将View添加到DecorView的mContentParent中
由于在步骤1
中已经创建并初始化了DecorView
,因此这一步直接将Activity
的视图添加到DecorView
的mContentParent
中即可:mLayoutInflater.inflate(layoutResID, mContentParent)
。到此为止,Activity
的布局文件已经添加到DecorView
里面了,由此可以理解Activity
的setContentView
这个方法的来历了。
3. 回调Activity的onContentChanged方法通知Activity视图已经发生改变
由于Activity实现了Window的Callback接口,这里表示Activity的布局文件已经被添加到DecorView的mContentParent中了,于是需要通知Activity,使其可以做相应的处理。Activity的onContentChanged方法是个空实现,我们可以在子Activity中处理这个回调。这个过程的代码如下所示:
final callback cb = getCallback();
if(cb != null && !isDestroyed()){
cb.onContentChanged();
}
到这里为止DecorView已经被创建并初始化完毕,Activity的布局文件也已经成功添加到了DecorView的mContentParent中,但是这个时候DecorView还没有被WindowManager正式添加到Window中,这里需要正确理解Window的概念,Window更多表示的是一种抽象的功能集合,虽然说早在Activity的attach方法中Window就已经被创建了,但是这个时候由于DecorView并没有被WindowManager识别,所以这个时候的Window无法提供具体功能,因为它还无法接收外界的输入信息。在ActivityThread的handleResumeActivity方法中,首先会调用Activity的onResume方法,接着会调用Activity的makeVisible(),正是在makeVisible()方法中,DecorView真正地完成了添加和显示这两个过程,到这里Activity的视图才能被用户看到。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}