我们先看一下Dialog的构造函数:
//构造函数
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == Resources.ID_NULL) {
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);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
可以看到,Dialog中和Activity一样,同样存在PhoneWindow,同样也提供了setContentView的方法,也是通过调用Window.setWindowManager去关联到WindowManager。
但是不同的是,Window.setWindowManager这个方法,后面两个参数是null。为什么呢?因为应用中常规的Dialog弹窗是依附与当前的Activity的,它的token和appName都是需要从Activity的Context中去获取的。
我们再来看show方法:
public void show() {
if (!mCreated) {
//调用到最后是一个空方法
dispatchOnCreate(null);
}
mDecor = mWindow.getDecorView();
WindowManager.LayoutParams l = mWindow.getAttributes();
//添加window
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
其实和ActivityThread中的handleResumeActivity很像,就是把DecorView添加到WindowManagerService中去。
只是在添加Dialog的DecorView的时候,WindowManagerGlobal会去调用Window的adjustLayoutParamsForSubWindow方法,Window又会根据LayoutParams的type类型,去调用DecorView的getWindowToken方法,获取token。
如果你想要在一个后台Servie中去弹出一个Dialog,那么这个时候LayoutParams的type类型必须是在FIRST_SYSTEM_WINDOW 和LAST_SYSTEM_WINDOW之间的系统层级类型:
else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
// We don't set the app token to this system window because the life cycles should be
// independent. If an app creates a system window and then the app goes to the stopped
// state, the system window should not be affected (can still show and receive input
// events).
if (curTitle == null || curTitle.length() == 0) {
final StringBuilder title = new StringBuilder(32);
title.append("Sys").append(wp.type);
if (mAppName != null) {
title.append(":").append(mAppName);
}
wp.setTitle(title);
}
}
也就是说,这个类型的Dialog并不需要依附于一个Activity,它有自己独立的生命周期。