重要なポイント
App Lifecycleは、モバイルアプリケーションのすべての可能な状態とそれらの間の遷移を記述する有限状態マシンです。各状態は、アプリがコードを実行できるか、UIを表示できるか、リソースを消費できるかを決定します。iOSとAndroidのモデルは似ていますが同一ではありません — 違いはOSアーキテクチャに起因します:iOSは厳格なメモリ制御(suspended状態)を使用し、AndroidはonSaveInstanceStateを通じた柔軟なプロセス管理を使用します。
iOSでは、ライフサイクルはフォアグラウンド/バックグラウンドに強く結びついています:アプリはアクティブかサスペンドのいずれかです。Androidでは、ライフサイクルはより細かく — Activity/ウィンドウは6つ以上の状態を経由し、プロセス自体がProcessLifecycleOwnerを通じて追加のライフサイクルを持ちます。現代のアプリケーション(2024年以降)は、ライフサイクルベースのアーキテクチャパターンをますます使用しています:SwiftUI Lifecycle(iOS 16+)は.scenePhase環境を通じて、Compose LifecycleはLifecycleEventObserverを通じて。これらのパターンは自動的にコンテキスト切り替えを処理し、メモリリークを防ぎます。Googleによると、Androidでの適切なライフサイクル処理は、バックグラウンド作業中のメモリ消費を30~40 %削減します。
iOSでは、アプリのライフサイクルはUIApplicationDelegateクラス(AppDelegate)を通じて管理されます。主なメソッド:application(_:didFinishLaunchingWithOptions:) — 初回起動時の初期化;applicationDidBecomeActive — アプリが表示され、操作可能;applicationWillResignActive — 非アクティブ状態への遷移(着信、通知シェードを閉じる);applicationDidEnterBackground — アプリが非表示、データ保存に約5秒;applicationWillEnterForeground — バックグラウンドから戻る;applicationWillTerminate — 終了(サスペンドがない古いアプリのみ呼ばれます)。
iOS 13以降、AppleはiPadとmacOS CatalystでのマルチウィンドウアプリケーションをサポートするためにSceneDelegate(UISceneDelegate)を導入しました。SceneDelegateは各シーン(ウィンドウ)の個別のライフサイクルを管理します:scene(_:willConnectTo:options:) — シーン作成;sceneDidBecomeActive — シーンがアクティブ;sceneWillResignActive — シーンがフォーカスを失う;sceneDidEnterBackground — シーンが非表示。AppDelegateはグローバルイベント(データ読み込み、プッシュ通知)を処理し、SceneDelegateは各ウィンドウのUI状態を処理します。SwiftUIでは、ライフサイクルは@Environment(\.scenePhase):.active、.inactive、.backgroundの環境値を介して処理されます — これによりSwiftUIとUIKitアプリケーションの両方に統一されたAPIが提供されます。
Androidでは、ライフサイクルはApplicationクラスから始まります — すべてのActivityより前に作成されるシングルトンです。Application.onCreate()メソッドは最初のエントリポイントで、グローバル初期化(Analytics、DI、Crash Reporting)に使用されます。その後、Activityが次の順序で起動します:onCreate() → onStart() → onResume()。最小化時:onPause() → onStop() → onSaveInstanceState()。破棄時:onDestroy()。復帰時:onRestart() → onStart() → onResume()。
ProcessLifecycleOwner(AndroidX Lifecycle 2.2+)は、個別のActivityではなくプロセス全体のライフサイクルを追跡します。2つの状態を提供します:ON_RESUME(アプリがフォアグラウンド)とON_STOP(アプリが非表示)。ProcessLifecycleOwnerはプロセスレベルで「アプリがいつバックグラウンドに移行したか」の問題を解決します — これはSDK、ライブラリ、モジュラーアプリケーションにとって重要です。Jetpack Composeでは、ライフサイクルはLifecycleEventObserverとcollectAsStateWithLifecycleを通じて管理され、バックグラウンドモードで自動的にデータ収集を中断します。Android 14(API 34)はService.onTimeoutを介したフォアグラウンドサービスのライフサイクル通知を追加しました — サービスは6時間以内に完了する必要があり、そうしないとシステムが強制的に停止します。
| iOS | Android | 説明 |
|---|---|---|
| Not Running | — | アプリが起動していない |
| didFinishLaunching | Application.onCreate | 起動時の最初の初期化 |
| Active | onResume | アプリが表示され入力を受け付けている |
| Inactive | onPause | 一時的なフォーカス喪失(着信、通知シェード) |
| Background | onStop | アプリが非表示、コードは実行可能 |
| Suspended | — | コードは実行されず、メモリは予約済み |
| Will Terminate | onDestroy | アプリが終了中 |
主な違い:iOSはバックグラウンドに入ってから5〜10秒後にアプリを強制的にSuspended(コード実行停止)に移行します。Androidはより長い期間のバックグラウンド作業(サービス、WorkManager)を許可しますが、Dozeモード(Android 6+)とバックグラウンド制限(Android 12+)の制限があります。AndroidのProcessLifecycleOwnerはiOSのUIApplicationDidEnterBackgroundNotificationに相当し、フォーカスを失った後だけでなく、バックグラウンドへの任意の遷移時に発火する点が異なります。
すべてのライフサイクル状態を処理する基本的なAppDelegate実装。バックグラウンド移行時のデータ保存と復帰時のUI更新。
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Analytics.shared.initialize()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
func applicationWillResignActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appWillResignActive, object: nil)
}
func applicationDidEnterBackground(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
UserDefaults.standard.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
NetworkManager.shared.refreshSession()
}
}
AppDelegateはライフサイクルの中核ポイントです。applicationDidEnterBackgroundは重要なメソッドです:iOSは保存に約5秒を与えます。applicationDidBecomeActive — UIを更新し、アニメーションを再開します。SceneDelegate(iOS 13+)では、ライフサイクルロジックはシーン全体に分散されます:sceneDidBecomeActive / sceneDidEnterBackgroundが個々のウィンドウを管理します。
マルチウィンドウiPadアプリケーション用のSceneDelegate。各シーンには、他のウィンドウから独立した独自のライフサイクルがあります。
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
func sceneDidBecomeActive(_ scene: UIScene) {
PlayerManager.shared.resume()
}
func sceneDidEnterBackground(_ scene: UIScene) {
PlayerManager.shared.pause()
CoreDataManager.shared.saveContext()
}
func sceneDidDisconnect(_ scene: UIScene) {
PlayerManager.shared.cleanup()
}
}
SceneDelegateは個別のシーンのライフサイクルを管理します。sceneDidBecomeActive / sceneDidEnterBackgroundは、特定のウィンドウのAppDelegateメソッドに相当します。sceneDidDisconnectはシーンが閉じられたときに呼ばれます(ユーザーがiPadでウィンドウを閉じました)。iOS 12以下との互換性のために、AppDelegateはSceneDelegateのロジックを複製する必要があります。iOS 17+では、@Observableを使用してscenePhaseに基づいてタスクを自動的に一時停止/再開できます。
ProcessLifecycleOwnerは、アプリがプロセスレベルでフォアグラウンド/バックグラウンドのどちらにあるかを追跡します。これはグローバルなライフサイクル管理に最適なアプローチです。
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
}
class AppLifecycleObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
Analytics.shared.onForeground()
NetworkMonitor.shared.start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
Analytics.shared.onBackground()
NetworkMonitor.shared.stop()
}
}
ProcessLifecycleOwnerは、アプリケーションレベルでバックグラウンドへの移行を追跡する唯一の正しい方法です。ON_STARTはアプリが表示されたとき(フォアグラウンド)に発火し、ON_STOP — 完全に非表示になったとき(バックグラウンド)に発火します。Activityのライフサイクルとは異なり、ProcessLifecycleOwnerは特定の画面に結びつきません。Jetpack Composeでは、手動監視の代わりにLifecycleResumeEffectまたはcollectAsStateWithLifecycleを使用してください。
ViewModel + Lifecycleは、ライフサイクル管理のための正しいAndroidアーキテクチャです。ViewModelはonDestroyで自動的にクリーンアップされ、リークを防ぎます。
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class TimerViewModel : ViewModel() {
private val _time = MutableLiveData<Long>()
val time: LiveData<Long> = _time
private var startTime = System.currentTimeMillis()
init {
updateTime()
}
private fun updateTime() {
_time.value = System.currentTimeMillis() - startTime
}
override fun onCleared() {
super.onCleared()
Logger.d("ViewModel cleared — リソースを解放")
}
}
// Activity内
class TimerActivity : AppCompatActivity() {
private val viewModel: TimerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Logger.d("Activity created")
}
override fun onResume() {
super.onResume()
viewModel.time.observe(this) { Logger.d("Time: $it") }
}
override fun onPause() {
super.onPause()
Logger.d("Activity paused — アニメーションを一時停止")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — クリーンアップ")
}
}
ViewModelは画面の回転を生き残り、Activityのfinish()時にのみ破棄されます。onCleared()はリソースの解放とストリームの購読解除のための場所です。LiveDataはonPauseで自動的に監視を一時停止し、onResumeで再開します — これによりバックグラウンド作業中のリークやクラッシュから保護されます。Composeでは、同じ目的でcollectAsStateWithLifecycle()を使用してください。
よくある質問
最小化すると、アプリはアクティブから非アクティブ(一時的に)、次にバックグラウンドに移行します。数秒後、システムはアプリをsuspended(サスペンド)に移行する可能性があります — コードは実行されず、メモリは予約されます。メモリが不足すると、システムはサスペンドされたアプリを終了します(willTerminateは呼ばれません)。applicationDidEnterBackgroundでデータを保存してください — これが保存のための最後の保証されたポイントです。
最低限必須:applicationDidEnterBackground — ユーザーデータを保存;applicationWillEnterForeground — UIを更新;AndroidのonPause — アニメーション/センサーを一時停止。ProcessLifecycleOwnerは処理を簡素化します:onResume — onPause(フォアグラウンド)、onStart — onStop(可視性)。必要に応じて他の状態を処理してください — すべての状態がすべてのアプリケーションにとって重要というわけではありません。
AppDelegateはアプリケーション全体のグローバルデリゲートです(起動、バックグラウンド移行)。SceneDelegate(iOS 13+)は個別のウィンドウ(シーン)のライフサイクルを管理します — マルチウィンドウiPadアプリケーションでは、各シーンに独自のライフサイクルがあります。iOS 12以下では、すべてのライフサイクルはAppDelegateを通じて処理されます。iPadOS 16+では、Stage ManagerにSceneDelegateが必要です。
ProcessLifecycleOwnerは、AndroidX Lifecycleのコンポーネントで、個別のActivityではなくアプリケーション全体(プロセス)のライフサイクルを追跡します。2つの主要なイベントを提供します:ON_RESUME(アプリがフォアグラウンド)とON_STOP(アプリがバックグラウンド)。SDK、アナリティクス、ネットワークモニターの初期化/クリーンアップに使用されます — 現在の画面に関係なく、アプリのバックグラウンド移行に反応する必要があるものすべてに使用します。
Jetpack Composeでは、LocalLifecycleOwner.current.lifecycleまたはcollectAsStateWithLifecycle()関数を介してLifecycleEventObserverを使用します。Compose画面では、LifecycleResumeEffectを使用します — DisposableEffectと同様に、onResumeで実行されonPauseでクリーンアップされます。lifecycleScope.launchWhenResumed — バックグラウンドで自動的に一時停止されるコルーチンです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。