1.activity的window创建
activity的window创建过程跟activity的启动过程息息相关,activity启动过程中最终会由ActivityThread中的performLaunchActivity()方法来完成整个启动,在该方法内部会通过类加载器加载创建activity的实例对象,并且通过attach方法为activity关联运行过程中所依赖的一些列上下文环境。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
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);
return activity;
}
在activity的attach方法中系统会创建activity所属的window对象,并为其设置回调接口,如下
//6.0以上源码
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
//5.0源码
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
可以看到不同版本的SDK源码在window创建这里是有点不一样,5.0以下的代码通PolicyManager.makeNewWindow方法来创建window对象,PolicyManager是一个策略类,它的真正实现是Policy类,该类中的makeNewWindow方法实现如下:
public Window makeNewWindow(Context context){
return new PhoneWindow(context);
}
从5.0版本源码可以看出最终也是new了一个PhoneWindow对象,而6.0的源码直接mWindow = new PhoneWindow(this, window);,其实两者本质是一样的,。到这里,window已经创建完了,下面分析activity的视图是如何显示在window上的。而 Activity 的视图是由 setContentView 提供,所以从 setContentView 入手,它的源码如下:
public void setContentView(int layoutResID){
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
从该方法可以看出,activity将具体的实现交给了window处理,而window的实现类是phonewindow,因此看phonewindow中的setContentView方法即可;
public void setContentView(View view, ViewGroup.LayoutParams params) {
// 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.
//1.如果没有DecorView 就创建它
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//将2.view添加到DecorView的mContentParent
//6.0的写法
mContentParent.addView(view, params);
//5.0的写法:
//mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
//3.回调onContentChanged方法通知activity视图已经发生改变
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
该方法有下面几个步骤:
1.如果没有DecorView 就创建它
DecorView 是activity中的顶级view,它的创建过程由installDecor方法来完成,该方法内部会通过generateDecor方法来直接创建DecorView ,这个时候DecorView 还是一个空白的FrameLayout, 接着,为了初始化DecorView 的结构,在installDecor方法内部还会调用generateLayout方法来加载具体的布局文件到DecorView 。
2.将view添加到DecorView的mContentParent
这一步直接将activity的视图添加到DecorView的mContentParent即可,注意5.0以前的源码跟以后源码的差异,不过本质都是相同的。
3.回调Activity的onContentChanged方法通知Activity视图已经发生改变
经过前面三个步骤,DecorView 已经被创建并且初始化完毕,Activity的布局文件也已经成功添加到了DecorView 的mContentParent中,但是这时DecorView 还没有没被WindowManager正式添加到window中。虽然在Activity的attach方法中window就已经被创建了,但是这个时候由于DecorView 并没有被WindowManager识别,所以这个时候的window无法提供具体功能,还无法接收外界的输入信息。在ActivityThread中的handleResumeActivity方法中,首先会调用activity的onResume方法,接着会调用activity的makeVisible方法,在该方法中,DecorView真正完成了添加和显示的过程,此时activity的视图才能被用户看到,
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
2.Dialog的Window创建
1.创建Window
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//6.0
final Window w = new PhoneWindow(mContext);
//5.0 Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
Dialog中window的创建在5.0中通过PolicyManager.makeNewWindow(mContext)来创建,创建后的对象实际就是PhoneWindow,6.0以后的源码则直接创建了PhoneWindow对象。
2.初始化DecorView 并将Dialog视图添加到DecorView
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
和activity的过程类似,也是通过window去添加
3.将DecorView 添加到Window中并显示出来
这个过程在Dialog的show方法中有体现
mWindowManager.addView(mDecor, l);
mShowing = true;
经过上面步骤Dialog的window创建完毕,它跟activity的window创建过程很类似。当dialog消失时,在dismiss方法中会通过 mWindowManager.removeViewImmediate(mDecor); 这句代码来移除DecorView
注意:普通的Dialog来创建时必须采用Activity的Context,如果采用Application的Context会报错,系统Window除外
参考:Android开发艺术探索