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開發藝術探索