maui-app-lifecycle

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

.NET MAUI App Lifecycle

.NET MAUI 应用生命周期

Handle application state transitions correctly in .NET MAUI. This skill covers the cross-platform Window lifecycle events, their platform-native mappings, and patterns for preserving state across backgrounding and resume cycles.
在.NET MAUI中正确处理应用状态转换。本技能涵盖跨平台Window生命周期事件、它们与原生平台的映射关系,以及在后台运行与恢复周期中保存状态的模式。

When to Use

适用场景

  • Saving or restoring state when the app backgrounds or resumes
  • Subscribing to Window lifecycle events (Created, Activated, Deactivated, Stopped, Resumed, Destroying)
  • Hooking into platform-native lifecycle callbacks via
    ConfigureLifecycleEvents
  • Deciding where to place initialization, teardown, or refresh logic
  • Understanding the difference between Deactivated and Stopped
  • 应用进入后台或恢复时保存/恢复状态
  • 订阅Window生命周期事件(Created、Activated、Deactivated、Stopped、Resumed、Destroying)
  • 通过
    ConfigureLifecycleEvents
    挂钩原生平台生命周期回调
  • 确定初始化、销毁或刷新逻辑的放置位置
  • 理解Deactivated与Stopped的区别

When Not to Use

不适用场景

  • Page-level navigation events — use Shell navigation guidance instead
  • Registering services at startup — use dependency injection guidance instead
  • Calling platform-specific APIs outside lifecycle context — use platform invoke guidance instead
  • 页面级导航事件——请改用Shell导航指南
  • 启动时注册服务——请改用依赖注入指南
  • 在生命周期上下文外调用平台特定API——请改用平台调用指南

Inputs

输入信息

  • The target lifecycle transition (e.g., "save draft when backgrounded", "refresh data on resume")
  • Which platforms the developer targets (Android, iOS, Mac Catalyst, Windows)
  • Whether the app uses multiple windows (iPad, Mac Catalyst, desktop Windows)
  • 目标生命周期转换(例如:“后台时保存草稿”、“恢复时刷新数据”)
  • 开发者目标平台(Android、iOS、Mac Catalyst、Windows)
  • 应用是否使用多窗口(iPad、Mac Catalyst、桌面端Windows)

App States

应用状态

A .NET MAUI app moves through four states:
StateDescription
Not RunningProcess does not exist
RunningForeground, receiving input
DeactivatedVisible but lost focus (dialog, split-screen, notification shade)
StoppedFully backgrounded, UI not visible
Typical flow: Not Running → Running → Deactivated → Stopped → Running (resumed) or Not Running (terminated).
.NET MAUI应用会经历四种状态:
状态描述
未运行进程不存在
运行中处于前台,接收输入
已失活可见但失去焦点(弹窗、分屏、通知栏)
已停止完全进入后台,UI不可见
典型流程:未运行 → 运行中 → 已失活 → 已停止 → 运行中(恢复)或未运行(终止)。

Window Lifecycle Events

Window生命周期事件

Microsoft.Maui.Controls.Window
exposes six cross-platform events:
EventFires when
Created
Native window allocated
Activated
Window receives input focus
Deactivated
Window loses focus (may still be visible)
Stopped
Window is no longer visible
Resumed
Window returns to foreground after Stopped
Destroying
Native window is being torn down
Microsoft.Maui.Controls.Window
公开六个跨平台事件:
事件触发时机
Created
原生窗口已分配
Activated
窗口获得输入焦点
Deactivated
窗口失去焦点(可能仍可见)
Stopped
窗口不再可见
Resumed
窗口从Stopped状态返回前台
Destroying
原生窗口正在被销毁

Subscribing via CreateWindow

通过CreateWindow订阅

Override
CreateWindow
in your
App
class and attach event handlers:
csharp
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;
    }
}
App
类中重写
CreateWindow
并附加事件处理程序:
csharp
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;
    }
}

Subscribing via a Custom Window Subclass

通过自定义Window子类订阅

Create a
Window
subclass and override the virtual methods:
csharp
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 */ }
}
Return it from
CreateWindow
:
csharp
protected override Window CreateWindow(IActivationState? activationState)
    => new AppWindow(new AppShell());
创建
Window
子类并重写虚拟方法:
csharp
public class AppWindow : Window
{
    public AppWindow(Page page) : base(page) { }

    protected override void OnActivated() { /* 刷新UI */ }
    protected override void OnStopped() { /* 保存状态 */ }
    protected override void OnResumed() { /* 恢复状态 */ }
    protected override void OnDestroying() { /* 清理资源 */ }
}
CreateWindow
返回该子类:
csharp
protected override Window CreateWindow(IActivationState? activationState)
    => new AppWindow(new AppShell());

Workflow: Save and Restore State on Background

工作流:后台时保存与恢复状态

  1. Identify transient state — draft text, scroll position, form inputs, timer values.
  2. Save in
    OnStopped
    — use
    Preferences
    for small values or file serialization for larger state.
  3. Restore in
    OnResumed
    — read back saved values and apply to your view model.
  4. Also save in
    OnDestroying
    on Android — the back button can skip
    Stopped
    entirely.
  5. Keep handlers fast — complete within 1–2 seconds to avoid ANR on Android or watchdog kills on iOS.
csharp
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);
}
  1. 识别临时状态——草稿文本、滚动位置、表单输入、计时器值。
  2. OnStopped
    中保存
    ——小型值使用
    Preferences
    ,大型状态使用文件序列化。
  3. OnResumed
    中恢复
    ——读取保存的值并应用到视图模型。
  4. 在Android的
    OnDestroying
    中也保存
    ——返回按钮可能会直接跳过
    Stopped
  5. 保持处理程序快速——在1-2秒内完成,避免Android出现ANR或iOS被看门狗杀死。
csharp
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返回按钮可能会跳过Stopped
    Preferences.Set("draft_text", _viewModel.DraftText);
}

Platform Lifecycle Mapping

平台生命周期映射

Android

Android

Window EventAndroid Callback
Created
OnCreate
Activated
OnResume
Deactivated
OnPause
Stopped
OnStop
Resumed
OnRestart
OnStart
OnResume
Destroying
OnDestroy
Window事件Android回调
Created
OnCreate
Activated
OnResume
Deactivated
OnPause
Stopped
OnStop
Resumed
OnRestart
OnStart
OnResume
Destroying
OnDestroy

iOS / Mac Catalyst

iOS / Mac Catalyst

Window EventUIKit Callback
Created
WillFinishLaunching
/
SceneWillConnect
Activated
DidBecomeActive
Deactivated
WillResignActive
Stopped
DidEnterBackground
Resumed
WillEnterForeground
Destroying
WillTerminate
Window事件UIKit回调
Created
WillFinishLaunching
/
SceneWillConnect
Activated
DidBecomeActive
Deactivated
WillResignActive
Stopped
DidEnterBackground
Resumed
WillEnterForeground
Destroying
WillTerminate

Windows (WinUI)

Windows (WinUI)

Window EventWinUI Callback
Created
OnLaunched
Activated
Activated
(foreground)
Deactivated
Activated
(background)
Stopped
VisibilityChanged
(false)
Resumed
VisibilityChanged
(true)
Destroying
Closed
Window事件WinUI回调
Created
OnLaunched
Activated
Activated
(前台)
Deactivated
Activated
(后台)
Stopped
VisibilityChanged
(false)
Resumed
VisibilityChanged
(true)
Destroying
Closed

Hooking Native Lifecycle Directly

直接挂钩原生生命周期

Use
ConfigureLifecycleEvents
in
MauiProgram.cs
when you need platform-specific callbacks beyond what Window events provide:
csharp
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
        .DidBecomeActive(app => Debug.WriteLine("iOS DidBecomeActive"))
        .WillResignActive(app => Debug.WriteLine("iOS WillResignActive"))
        .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
});
当需要Window事件之外的平台特定回调时,在
MauiProgram.cs
中使用
ConfigureLifecycleEvents
csharp
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
        .DidBecomeActive(app => Debug.WriteLine("iOS DidBecomeActive"))
        .WillResignActive(app => Debug.WriteLine("iOS WillResignActive"))
        .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
});

Common Pitfalls

常见陷阱

  1. Resumed does not fire on first launch. The initial sequence is
    Created
    Activated
    . Use
    OnActivated
    for logic that must run on every foreground entry, not
    OnResumed
    .
  2. Deactivated ≠ Stopped. A dialog, split-screen, or notification pull-down triggers
    Deactivated
    without
    Stopped
    . Do not perform heavy saves in
    OnDeactivated
    — the app may never actually background.
  3. Android back button skips Stopped. On Android, pressing back may call
    Destroying
    directly without
    Stopped
    . Place critical save logic in both
    OnStopped
    and
    OnDestroying
    .
  4. Multi-window apps fire events independently. On iPad, Mac Catalyst, and desktop Windows each
    Window
    instance fires its own lifecycle events. Do not assume a single global lifecycle.
  5. Long-running handlers cause kills. Android enforces a ~5 second ANR timeout; iOS has limited background execution time. Keep lifecycle handlers synchronous and fast — use
    Preferences
    for quick saves, not database writes.
  6. Do not use legacy Xamarin.Forms lifecycle methods.
    Application.OnStart()
    ,
    Application.OnSleep()
    , and
    Application.OnResume()
    exist for backward compatibility but bypass Window-level events. In .NET MAUI, prefer
    Window
    lifecycle events (
    OnActivated
    ,
    OnStopped
    ,
    OnResumed
    , etc.) for correct multi-window behavior.
  1. 首次启动时不会触发Resumed。初始序列是
    Created
    Activated
    。对于必须在每次进入前台时运行的逻辑,请使用
    OnActivated
    而非
    OnResumed
  2. Deactivated ≠ Stopped。弹窗、分屏或下拉通知会触发
    Deactivated
    但不会触发
    Stopped
    。不要在
    OnDeactivated
    中执行大量保存操作——应用可能并未真正进入后台。
  3. Android返回按钮会跳过Stopped。在Android上,按下返回按钮可能直接调用
    Destroying
    而不经过
    Stopped
    。请将关键保存逻辑同时放在
    OnStopped
    OnDestroying
    中。
  4. 多窗口应用会独立触发事件。在iPad、Mac Catalyst和桌面端Windows上,每个
    Window
    实例都会触发自己的生命周期事件。不要假设存在单一全局生命周期。
  5. 长时间运行的处理程序会导致应用被杀死。Android强制执行约5秒的ANR超时;iOS的后台执行时间有限。保持生命周期处理程序同步且快速——使用
    Preferences
    进行快速保存,而非数据库写入。
  6. 不要使用旧版Xamarin.Forms生命周期方法
    Application.OnStart()
    Application.OnSleep()
    Application.OnResume()
    仅为向后兼容而存在,但会绕过Window级事件。在.NET MAUI中,为了实现正确的多窗口行为,优先使用
    Window
    生命周期事件(
    OnActivated
    OnStopped
    OnResumed
    等)。