maui-app-lifecycle について
.NET MAUI のライフサイクルイベントと状態を扱う開発者向けのスキルです。イベントの違い、ウィンドウごとの動作、保存と復元の時機、ハンドラーの性能制約を説明します。
主な機能
- 01アクティブ化・再開・停止イベントの区別
- 02Android の戻るボタンによる破棄への対応
- 03ウィンドウ単位のライフサイクル管理
- 04ハンドラーのブロック回避
- 05状態の保存と復元の計画
- 06iOS シーンライフサイクルの把握
活用シーン
- 01初回起動時に実行されない初期化処理の修正
- 02アプリ状態を保存するタイミングの選定
- 03マルチウィンドウアプリでの状態管理
- 04処理の遅いハンドラーによる終了リスクの低減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-app-lifecycleインストール後、Claude Code で/maui-app-lifecycleを入力して読み込まれたか確認します。
同梱ファイルreferences/lifecycle-api.md
詳細
.NET MAUI アプリのライフサイクル
注意が必要な動作
⚠️ Resumed ≠ 初回起動
Resumed は Stopped 状態から戻ったときにのみ発生します。初回起動時のシーケンスは Created → Activated であり、Resumed は呼び出されません。
// ❌ Putting initialization logic in OnResumed — won't run on first launch
protected override void OnResumed()
{
LoadUserProfile(); // Skipped on cold start!
}
// ✅ Use OnActivated for logic that must run on every foreground entry
protected override void OnActivated()
{
LoadUserProfile(); // Runs on both first launch and resume
}
⚠️ Deactivated ≠ Stopped
ダイアログ、画面分割、通知パネルのプルダウンでは、Stopped が発生せずに Deactivated が発生します。アプリが実際にバックグラウンドへ移行するとは限らないため、Deactivated で大きな状態を保存しないでください。
// ❌ Heavy save on every deactivation — fires too often
protected override void OnDeactivated()
{
await SaveAllDataToDatabase(); // Wasteful for a dialog appearance
}
// ✅ Save state only when truly backgrounded
protected override void OnStopped()
{
await SaveAllDataToDatabase();
}
⚠️ Android の戻るボタンでは Stopped が発生しない
Android では、アクティビティが終了すると、ハードウェアの戻るボタンを押したときに Stopped を経ずに Destroying が呼び出されることがあります。OnStopped のみで重要な保存処理を行うと、実行されない場合があります。
// ✅ Save in both Stopped and Destroying for safety on Android
protected override void OnStopped()
{
base.OnStopped();
SaveDraft();
}
protected override void OnDestroying()
{
base.OnDestroying();
SaveDraft(); // Catches Android back-button finish
}
⚠️ 複数ウィンドウでは個別に発生する
iPad、Mac Catalyst、デスクトップ版 Windows では、各 Window インスタンスのライフサイクルイベントが個別に発生します。ライフサイクルがグローバルに1つだけだと思い込まないでください。
パフォーマンス:ハンドラーを高速に保つ
ライフサイクルハンドラーで時間のかかる処理を行うと、Android では ANR による終了(タイムアウトは5秒)、iOS では ウォッチドッグによる終了(バックグラウンドでの実行時間に制限あり) の原因になります。
// ❌ Blocking the lifecycle handler
protected override void OnStopped()
{
Thread.Sleep(3000); // ANR on Android!
SaveData();
}
// ✅ Fire-and-forget or use a brief async save
protected override void OnStopped()
{
base.OnStopped();
Preferences.Set("draft_text", _viewModel.DraftText); // Fast, synchronous
}
大きな状態を保存する場合は、SecureStorage またはファイルベースのシリアル化を使用してください。ただし、処理は1~2秒以内に完了させてください。
iOS のシーンライフサイクル
iOS 13 以降では、シーンライフサイクル(SceneWillConnect など)が使用されます。古いデリゲートメソッドも MAUI 経由で引き続き呼び出されますが、最新の iOS ではシーンベースの API を対象にしてください。
状態保持のチェックリスト
- 一時的な UI 状態(スクロール位置、下書きテキスト)を
OnStoppedで保存する -
OnActivatedではなくOnResumedで状態を復元する(二重復元を避ける) -
OnDeactivatedでは重い I/O を行わない(発生頻度が高すぎます) - Android:戻るボタンの場合に備え、重要な保存処理を
OnDestroyingでも行う - ライフサイクルハンドラーを2秒以内に完了させる
- マルチウィンドウアプリでは、ウィンドウごとに個別に状態を処理する