关于 maui-app-lifecycle
.NET MAUI App Lifecycle 是面向 .NET MAUI 开发者的技能,说明应用窗口状态变化及跨平台生命周期事件。它涵盖原生回调映射和应用进入后台、恢复时的状态保存方法。
主要功能
- 01说明六种窗口生命周期事件
- 02对照 Android、iOS、Mac Catalyst 和 Windows 回调
- 03讲解后台保存与恢复状态
- 04演示订阅窗口事件和重写方法
- 05说明 ConfigureLifecycleEvents 的用法
- 06指出多窗口及生命周期常见陷阱
适用场景
- 01应用切到后台时保存草稿或表单
- 02应用恢复前台时刷新界面或还原状态
- 03需要接入平台原生生命周期回调
- 04区分窗口失焦与应用进入后台
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill maui-app-lifecycle安装后在 Claude Code 中输入/maui-app-lifecycle确认已加载。
附带文件references/lifecycle-api.md
详细介绍
.NET MAUI 应用生命周期
在 .NET MAUI 中正确处理应用状态转换。本技能介绍跨平台的 Window 生命周期事件、它们与平台原生事件的映射关系,以及在应用进入后台和恢复时保留状态的模式。
适用场景
- 应用进入后台或恢复时保存或还原状态
- 订阅 Window 生命周期事件(Created、Activated、Deactivated、Stopped、Resumed、Destroying)
- 通过
ConfigureLifecycleEvents挂接平台原生生命周期回调 - 决定将初始化、清理或刷新逻辑放在哪里
- 了解 Deactivated 和 Stopped 之间的区别
不适用场景
- 页面级导航事件——请改用 Shell 导航指南
- 在启动时注册服务——请改用依赖注入指南
- 在生命周期上下文之外调用特定于平台的 API——请改用平台调用指南
输入
- 目标生命周期转换(例如“应用进入后台时保存草稿”“恢复时刷新数据”)
- 开发者目标平台(Android、iOS、Mac Catalyst、Windows)
- 应用是否使用多个窗口(iPad、Mac Catalyst、桌面版 Windows)
应用状态
.NET MAUI 应用会经历四种状态:
| 状态 | 描述 |
|---|---|
| 未运行 | 进程不存在 |
| 运行中 | 位于前台,正在接收输入 |
| 已停用 | 仍然可见,但已失去焦点(对话框、分屏、通知栏) |
| 已停止 | 完全进入后台,UI 不可见 |
典型流程:未运行 → 运行中 → 已停用 → 已停止 → 运行中(已恢复)或未运行(已终止)。
Window 生命周期事件
Microsoft.Maui.Controls.Window 提供六个跨平台事件:
| 事件 | 触发时机 |
|---|---|
Created | 已分配原生窗口 |
Activated | 窗口获得输入焦点 |
Deactivated | 窗口失去焦点(可能仍然可见) |
Stopped | 窗口不再可见 |
Resumed | 窗口在 Stopped 后返回前台 |
Destroying | 正在销毁原生窗口 |
通过 CreateWindow 订阅
在 App 类中重写 CreateWindow 并附加事件处理程序:
public partial class App : Application
{
protected override Window CreateWindow(IActivationState? activationState)
{
var window = base.CreateWindow(activationState);
window.Created += (s, e) => Debug.WriteLine("Created");
window.Activated += (s, e) => Debug.WriteLine("Activated");
window.Deactivated += (s, e) => Debug.WriteLine("Deactivated");
window.Stopped += (s, e) => Debug.WriteLine("Stopped");
window.Resumed += (s, e) => Debug.WriteLine("Resumed");
window.Destroying += (s, e) => Debug.WriteLine("Destroying");
return window;
}
}
通过自定义 Window 子类订阅
创建 Window 子类并重写虚方法:
public class AppWindow : Window
{
public AppWindow(Page page) : base(page) { }
protected override void OnActivated() { /* refresh UI */ }
protected override void OnStopped() { /* save state */ }
protected override void OnResumed() { /* restore state */ }
protected override void OnDestroying() { /* cleanup */ }
}
从 CreateWindow 返回该子类:
protected override Window CreateWindow(IActivationState? activationState)
=> new AppWindow(new AppShell());
工作流:在应用进入后台时保存和还原状态
- 识别临时状态——草稿文本、滚动位置、表单输入、计时器值。
- 在
OnStopped中保存——小型值使用Preferences,较大的状态使用文件序列化。 - 在
OnResumed中还原——读取已保存的值并应用到视图模型。 - 在 Android 上也要在
OnDestroying中保存——返回按钮可能会完全跳过Stopped。 - 确保处理程序运行迅速——在 1–2 秒内完成,以避免 Android 上发生 ANR,或 iOS 上被看门狗终止。
protected override void OnStopped()
{
base.OnStopped();
Preferences.Set("draft_text", _viewModel.DraftText);
Preferences.Set("scroll_y", _viewModel.ScrollY);
}
protected override void OnResumed()
{
base.OnResumed();
_viewModel.DraftText = Preferences.Get("draft_text", string.Empty);
_viewModel.ScrollY = Preferences.Get("scroll_y", 0.0);
}
protected override void OnDestroying()
{
base.OnDestroying();
// Android back-button can skip Stopped
Preferences.Set("draft_text", _viewModel.DraftText);
}
平台生命周期映射
Android
| Window 事件 | Android 回调 |
|---|---|
| Created | OnCreate |
| Activated | OnResume |
| Deactivated | OnPause |
| Stopped | OnStop |
| Resumed | OnRestart → OnStart → OnResume |
| Destroying | OnDestroy |
iOS / Mac Catalyst
| Window 事件 | UIKit 回调 | AddiOS 生成器方法 |
|---|---|---|
| Created | WillFinishLaunching / SceneWillConnect | .WillFinishLaunching() / .SceneWillConnect() |
| Activated | DidBecomeActive | .OnActivated() |
| Deactivated | WillResignActive | .OnResignActivation() |
| Stopped | DidEnterBackground | .DidEnterBackground() |
| Resumed | WillEnterForeground | .WillEnterForeground() |
| Destroying | WillTerminate | .WillTerminate() |
⚠️ UIKit 选择器名称与
AddiOS生成器方法名称在激活事件上有所不同。不存在.DidBecomeActive()或.WillResignActive()生成器方法——请使用.OnActivated()和.OnResignActivation(),否则代码将无法编译。
Windows(WinUI)
| 窗口事件 | WinUI 回调 |
|---|---|
| 创建 | OnLaunched |
| 激活 | Activated(前台) |
| 停用 | Activated(后台) |
| 停止 | VisibilityChanged(false) |
| 恢复 | VisibilityChanged(true) |
| 销毁 | Closed |
直接挂接原生生命周期
当你需要使用 Window 事件未提供的平台专属回调时,请在 MauiProgram.cs 中使用 ConfigureLifecycleEvents:
builder.ConfigureLifecycleEvents(events =>
{
#if ANDROID
events.AddAndroid(android => android
.OnCreate((activity, bundle) => Debug.WriteLine("Android OnCreate"))
.OnResume(activity => Debug.WriteLine("Android OnResume"))
.OnPause(activity => Debug.WriteLine("Android OnPause"))
.OnStop(activity => Debug.WriteLine("Android OnStop"))
.OnDestroy(activity => Debug.WriteLine("Android OnDestroy")));
#elif IOS || MACCATALYST
events.AddiOS(ios => ios
.OnActivated(app => Debug.WriteLine("iOS OnActivated"))
.OnResignActivation(app => Debug.WriteLine("iOS OnResignActivation"))
.DidEnterBackground(app => Debug.WriteLine("iOS DidEnterBackground"))
.WillEnterForeground(app => Debug.WriteLine("iOS WillEnterForeground")));
#elif WINDOWS
events.AddWindows(windows => windows
.OnLaunched((app, args) => Debug.WriteLine("Windows OnLaunched"))
.OnActivated((window, args) => Debug.WriteLine("Windows Activated"))
.OnClosed((window, args) => Debug.WriteLine("Windows Closed")));
#endif
});
常见陷阱
-
首次启动时不会触发 Resumed。 初始事件序列为
Created→Activated。对于每次进入前台时都必须运行的逻辑,请使用OnActivated,而不是OnResumed。 -
Deactivated 不等于 Stopped。 对话框、分屏或下拉通知会触发
Deactivated,但不会触发Stopped。不要在OnDeactivated中执行耗时的保存操作——应用可能根本不会进入后台。 -
Android 返回按钮会跳过 Stopped。 在 Android 上,按下返回按钮可能会直接调用
Destroying,而不触发Stopped。请将关键保存逻辑放在OnStopped和OnDestroying中。 -
多窗口应用会独立触发事件。 在 iPad、Mac Catalyst 和桌面版 Windows 上,每个
Window实例都会触发生命周期事件。不要假设只有一个全局生命周期。 -
长时间运行的处理程序可能导致应用被终止。 Android 强制执行约 5 秒的 ANR 超时;iOS 的后台执行时间有限。请确保生命周期处理程序同步且快速——快速保存请使用
Preferences,而不要写入数据库。 -
不要使用旧版 Xamarin.Forms 生命周期方法。
Application.OnStart()、Application.OnSleep()和Application.OnResume()为向后兼容而保留,但会绕过 Window 级别的事件。在 .NET MAUI 中,请优先使用Window生命周期事件(OnActivated、OnStopped、OnResumed等),以确保多窗口行为正确。