1.当我们查看android dialog源码,首先查看dialog的构造方法,初始化相关数据。由源码可知Dialog获取当前的themeId,通过 new ContextThemeWrapper(context, themeResId);设置主题返回Context对象。通过new PhoneWindow()创建Window对象,设置Window相关方法,
public Dialog(@NonNull Context context) {
this(context, 0, true);
}
public Dialog(@NonNull Context context, @StyleRes int themeResId) {
this(context, themeResId, true);
}
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == ResourceId.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);
}
Dialog创建类似activity创建,通过new PhoneWidow对象,我们查看以下源码调用setContentView()方法可知通过Window实现类PhoneWindow将布局文件添加到DectorView中。
//以下是Window相关的代码
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
//以下是PhoneWindow的相关的代码
public void setContentView(int layoutResID) {
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();
}
mContentParentExplicitlySet = true;
}
2.查看dialog的show()方法,在show()方法中,判断当前dialog是否已经显示,调用oncreate(),onStart(),获取getDecorView(),通过windowManager添加DectorView,发送消息给主线程通知弹窗已经show.
public void show() {
if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
mCanceled = false;
if (!mCreated) {
dispatchOnCreate(null);
} else {
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
onStart();
mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
final ApplicationInfo info = mContext.getApplicationInfo();
mWindow.setDefaultIcon(info.icon);
mWindow.setDefaultLogo(info.logo);
mActionBar = new WindowDecorActionBar(this);
}
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
当我们查看dismissDisDialog()方法时,可知通过widowManager移除DectorView,mWindowManager.removeViewImmediate(mDecor);
void dismissDialog() {
if (mDecor == null || !mShowing) {
return;
}
if (mWindow.isDestroyed()) {
Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
return;
}
try {
mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
onStop();
mShowing = false;
sendDismissMessage();
}
}
以下是消息处理,弹窗显示,取消,消失。
Private static final class ListenersHandler extends Handler {
private final WeakReference<DialogInterface> mDialog;
public ListenersHandler(Dialog dialog) {
mDialog = new WeakReference<>(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DISMISS:
((OnDismissListener) msg.obj).onDismiss(mDialog.get());
break;
case CANCEL:
((OnCancelListener) msg.obj).onCancel(mDialog.get());
break;
case SHOW:
((OnShowListener) msg.obj).onShow(mDialog.get());
break;
}
}
}
以上就是Dialog创建和消失的过程,普通Dialog的Context必须通过Activity持有的Context才可以,不然会报异常,报错信息是没有应用token所致,而应用token是Activity持有的,系统弹窗不需要token,在WindowManager.LayoutParams中的type表示window的类型,系统层级范围在2000~2999。若传Application 的Context对象,可以使用如下所示:
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
在Manifest声明权限可以使用系统Window:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Q:弹出dialog对Activity生命周期有影响吗?
我们知道,生命周期的回调都是AMS通过bInder通知应用进程执行的,而弹出的Dialog,Toast,PopupWindow本质上都是直接通过WMS.addView()显示的,没有经过AMS,所以不会对Activity生命周期有任何影响。
如果是启动一个Theme为dialog的Activity,则生命周期为:
A.onPause->B.onCreate->B.onStart->B.onResume
注意这边没有前一个Activity不会调用A.onStop,因为只有A切换到后台不可见的时候才会调用A.onStop;而弹出Dialog主题的Activity时前一个页面还是可见的,只是失去了焦点,所以会调用A.onPause
网友评论