让我们把 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 的生命周期有了更清晰、更深入的理解!