让我们把 Dialog 的生命周期想象成一个舞台剧演员的登场、表演和谢幕过程,并结合核心源码来讲解。记住,Dialog 的生命周期比 Activity 简单得多,核心围绕着 创建、显示、消失 这几个关键节点。
主角: AlertDialog (最常见的 Dialog)
导演: Dialog 类
舞台监督: Window / WindowManager
关键舞台: DecorView (承载 Dialog 内容的根 View)
核心生命周期方法: onCreate(), onStart(), onStop(), dismiss(), show()
故事开始:
-
剧本创作与角色准备 (
new AlertDialog.Builder().create())-
你作为导演 (
Builder),写好剧本(设置标题、内容、按钮等)。 -
调用
create()。这相当于导演喊:"演员 AlertDialog,准备出场!" -
幕后: 此时
AlertDialog对象在内存中被创建 ,但就像一个在后台化妆准备的演员,观众还看不见它 。它内部的Window对象也被创建,但还没添加到真正的窗口管理系统中。 -
源码关键点 (
Dialog类):java
lessDialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { // ... 获取 WindowManager, 初始化主题等 ... mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // 关键:创建一个属于这个 Dialog 的 Window (PhoneWindow) final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); // Dialog 自己处理 Window 事件 w.setOnWindowDismissedCallback(this); // 监听窗口消失 w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); // 默认居中 }
-
-
演员登台亮相 (
dialog.show())-
导演喊:"Action!演员上台!" (
show()被调用)。 -
演员 (
AlertDialog) 正式走上舞台。 -
幕后:
-
onCreate()被调用: 这是演员上台后的第一个正式动作 。在这里,Dialog 会根据你之前在Builder里设置的"剧本",真正创建并组装它的 UI 内容 (比如setContentView()加载布局,初始化按钮、文本等)。就像演员开始按照剧本布置舞台上的道具和站位。java
typescript// AlertDialog 类 (继承 Dialog) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 关键:这里会根据 Builder 的设置,创建 AlertController 并设置内容 View mAlert.installContent(); // 内部会调用 setContentView() } -
onStart()被调用: UI 准备就绪后,onStart()被调用。这标志着 Dialog 对用户可见了 ,就像舞台灯光亮起,观众可以看到演员了。这时可以开始一些需要在显示期间持续运行的操作(比如启动动画、注册广播接收器、开始监听传感器 - 但要小心内存泄漏!)。java
csharp// Dialog 类 public void onStart() { // 空实现,通常由子类覆盖执行需要在显示时开始的操作 } -
关键一步 - 挂载到窗口系统 (
WindowManager.addView()): 这是 Dialog 能显示在屏幕上的核心技术步骤 。Dialog会获取自己的Window对象,并从中取出代表其整个 UI 的根视图DecorView。然后,它通过WindowManager.addView(decorView, params)将这个DecorView添加 到系统的窗口管理服务中。系统服务会负责测量、布局、绘制这个DecorView,最终将其内容合成到屏幕上。就像演员站到了舞台中央指定的聚光灯下。java
scss// Dialog 类 - show() 方法的关键部分 public void show() { // ... 省略部分检查代码 ... // 1. 分发即将显示的事件 (较少用) onStart(); // 2. 关键步骤:将 Window 的 DecorView 添加到 WindowManager mDecor = mWindow.getDecorView(); // 获取根视图 WindowManager.LayoutParams l = mWindow.getAttributes(); mWindowManager.addView(mDecor, l); // 挂载到窗口系统! // 3. 设置 Dialog 为显示状态 mShowing = true; // 4. 发送显示消息 (较少用) sendShowMessage(); }
-
-
-
演员表演中 (
Dialog显示状态)- 演员在舞台上表演(Dialog 显示在屏幕上)。
- 用户可以与其交互(点击按钮、输入文本等)。
- 这个阶段 Dialog 是活跃 (Active) 的。它持有
Window和DecorView,并响应事件。
-
演员谢幕 (
dialog.dismiss()或dialog.cancel())-
表演结束,导演喊:"Cut!演员谢幕!" (用户点击按钮触发
dismiss(),或点击外部/按返回键触发cancel())。 -
幕后:
-
dismiss()核心动作: 这是主要的谢幕方式。它的核心任务就是**请求系统把这个 Dialog 的根视图 (DecorView) 从窗口系统中移除 (WindowManager.removeViewImmediate()) **。就像让演员立刻从舞台中央的聚光灯下离开。java
scss// Dialog 类 - dismiss() 方法核心 public void dismiss() { if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); // 如果在主线程直接执行 } else { mHandler.post(mDismissAction); // 否则 post 到主线程 } } private void dismissDialog() { if (mDecor == null || !mShowing) { return; } // 关键步骤 1: 从 WindowManager 移除 DecorView mWindowManager.removeViewImmediate(mDecor); // ... 清理状态 (mDecor = null, mShowing = false) ... // 关键步骤 2: 发送关闭消息 (触发 onStop 等) sendDismissMessage(); } -
cancel()的区别: 它本质上也是调用dismiss(),但它额外做了一件事 :在dismiss()之前,会触发DialogInterface.OnCancelListener(如果设置了的话)。这就像是演员在正式离开舞台前,额外向观众鞠躬致意(执行取消监听器)。java
scss// Dialog 类 - cancel() public void cancel() { // 1. 触发 OnCancelListener (如果设置了) if (mCancelMessage != null) { Message m = Message.obtain(mCancelMessage); m.sendToTarget(); } // 2. 核心还是调用 dismiss() dismiss(); } -
onStop()被调用: 在DecorView从窗口系统移除之后 ,onStop()被调用。这标志着 Dialog 完全从用户视野中消失 。就像演员已经走下舞台,回到了后台。这里是释放资源的关键时机:- 停止在
onStart()中开始的动画或后台任务。 - 取消注册在
onStart()中注册的广播接收器、监听器等。 - 进行一些清理工作。
java
csharp// Dialog 类 public void onStop() { // 空实现,通常由子类覆盖执行需要在消失后停止的操作 } - 停止在
-
-
-
演员卸妆离场 (
Dialog对象等待回收)- 演员 (
Dialog对象) 虽然已经从舞台 (屏幕) 上消失,但它可能还在后台 (内存) 中(比如你持有它的引用)。它不再持有Window和DecorView(它们已被销毁),就像一个卸了妆的演员。 - 如果没有任何地方再引用这个
Dialog对象,垃圾回收器 (GC) 最终会将其从内存中清除,完成整个生命周期的终结。
- 演员 (
生命周期流程图(简化版)
text
sql
+-------------------+ +-----------------+
| new & create() | | |
| (对象创建, 未显示) | | |
+-------------------+ | |
| | |
v (show()) | |
+-------------------+ | |
| onCreate() | <-----+ |
| (创建UI内容View) | | |
+-------------------+ | Dialog 显示中 |
| | (用户可见可交互) |
v | |
+-------------------+ | |
| onStart() | <-----+ |
| (UI可见, 开始操作) | | |
+-------------------+ | |
| | |
| (添加到Window) | |
+----------------->+ |
| |
| |
(dismiss()/cancel()) |
+<-----------------+ |
| | |
+-------------------+ | |
| 移除View (核心) | | |
| (从WindowManager) | +-----------------+
+-------------------+ |
| |
v |
+-------------------+ |
| onStop() | <-----+
| (UI不可见, 停止操作)|
+-------------------+
|
v
+-------------------+
| 对象可能还在内存 |
| (等待GC回收) |
+-------------------+
关键点总结 (结合故事与源码):
-
show()触发显示流程:onCreate():一次性的 UI 内容创建 (setContentView, 初始化View)。发生在show()调用链中。onStart():Dialog 对用户可见 ,执行开始操作 (动画、注册监听)。紧接在onCreate()之后,也在show()调用链中。WindowManager.addView(mDecor):这是 Dialog 真正显示到屏幕上的核心步骤 ,发生在show()方法内部。mDecor是 Dialog 的根视图。
-
dismiss()/cancel()触发消失流程:WindowManager.removeViewImmediate(mDecor):这是 Dialog 从屏幕上消失的核心步骤 ,发生在dismiss()调用链中。移除DecorView。onStop():Dialog 完全不可见 ,执行停止操作 (停止动画、注销监听)。发生在移除DecorView之后。cancel():在调用dismiss()之前,会额外触发OnCancelListener。
-
没有
onDestroy(): Dialog 没有像 Activity 那样明确的onDestroy()。对象的销毁依赖垃圾回收。在onStop()中做好资源释放就足够了。 -
WindowManager 是关键桥梁:
addView()和removeViewImmediate()是控制 Dialog 在屏幕上生死的核心方法,它们操作的对象是DecorView。 -
生命周期方法用途:
onCreate():构建 UI (只调用一次)。onStart():处理显示 时需要开始的事情 (每次show()都可能调用)。onStop():处理消失 时需要结束的事情 (每次dismiss()都可能调用)。
开发注意事项:
- 资源管理: 务必在
onStop()中释放/停止在onStart()中启动的资源(如动画、广播、监听器),否则会导致内存泄漏或冗余操作。 - 避免泄漏: 不要在 Dialog 内部持有 Activity/Fragment 的强引用(可以使用弱引用或确保在
onStop()/dismiss()中解除引用)。因为 Dialog 可能比 Activity 存活得更久(比如在 Activity 配置变更时)。 dismiss()vscancel(): 理解它们的区别,按需使用。通常dismiss()是基础操作,cancel()用于需要额外处理"取消"语义的场景。- UI 操作时机: 在
onCreate()中才能安全操作 Dialog 内部的 View。在show()之前调用findViewById可能返回null。
通过这个"舞台剧"的比喻和核心源码的剖析,希望你对 Dialog 的生命周期有了更清晰、更深入的理解!