Not Running — とは何か、ライフサイクルの初期状態

著者: IT Sectr 公開日: 2026-03-03 読了時間: 11 分

Not Running — まだ起動されていないか、すでに終了したモバイルアプリケーションのライフサイクルの初期状態。 iOSおよびAndroidがこの状態をどのように管理するか、どのようなイベントがNot Runningからの遷移を引き起こすか、SwiftおよびKotlinでアプリケーションの起動と終了を正しく処理する方法を学びましょう。

ポイント

  • Not Running — アプリケーションがメモリにロードされておらず、コードを実行していない、ライフサイクルの入り口であり出口でもある
  • 起動 — Not Runningからの遷移はアプリアイコンをタップするか、ディープリンクまたはプッシュ通知によって起こる
  • 終了 — ユーザーがスワイプでアプリを閉じ、システムがメモリ不足でアンロードするか、クラッシュが発生する
  • コールドスタート — アプリケーションがゼロから起動し、すべてのオブジェクトが新たに作成され、状態がキャッシュから復元されない
  • ホットスタート — アプリケーションがSuspended状態にあり、完全な初期化なしでActiveに戻る

Not Running — この状態とは

Not Running は、モバイルアプリケーションがデバイスのRAMにロードされておらず、システム資源を消費しない、ライフサイクルの基本状態です。 iOSやAndroidでは、この状態はアプリケーションに関連するプロセスやスレッドが完全に存在しないことを意味します。ユーザーはホーム画面にアプリアイコンを見ていますが、アプリケーション自体はアクティブではなく、最近使用したアプリの一覧にもありません。

ユーザーがアプリアイコンをタップすると、システムは新しいプロセスを作成し、実行可能コードをメモリにロードし、必要なデータ構造をすべて初期化します。 このプロセスをコールドスタートと呼びます。ロード時間の観点から最もリソースを消費するプロセスです。

システムは、他のどんな状態からでもアプリケーションをNot Runningに移動できます。アプリケーションがバックグラウンドまたはSuspendedにある場合、オペレーティングシステムはより優先度の高いタスク(例えばフォアグラウンドのアクティブなアプリケーション)のために十分なRAMがない場合、アンロードする権利を有します。

開発者は、アプリケーションがバックグラウンドにあるとき、システムによって何時でも終了される可能性があることを考慮する必要があります。これは、保存されていないすべてのデータが失われる可能性があることを意味します。そのため、ActiveからBackgroundへの遷移中に、キーバリューストア(UserDefaults、SharedPreferences)またはローカルデータベースに状態を保存することが極めて重要です。

システムはどのようにアンロードするアプリケーションを決定するか

iOSは、現在のアプリケーションの状態に基づいて優先度を使用します。Activeが最も高く、次いでInactive、Background、Suspended、そして最後にNot Runningが最も低くなります。Androidは似たようなプロセス階層を使用します。ForegroundプロセスはOOM_ADJ = 0、Visibleプロセス = 100、Serviceプロセス = 200、Backgroundプロセス = 300、Emptyプロセス = 400の優先度です。値が高いほど、メモリが不足しているときにプロセスが終了される可能性が高くなります。

プラットフォーム状態アンロード優先度説明
iOSNot Running最高アプリがロードされていない — システムリソースを消費しない
iOSSuspended高いアプリがメモリにあるがコードを実行していない — アンロードの第一目標
iOSBackground中程度アプリがバックグラウンドタスクを実行中 — タイムアウト後にアンロード
iOSActive低いアクティブなアプリ — メモリの危機的不足時にのみアンロード
AndroidEmpty Process最高アクティブなコンポーネントのないプロセス — 最初に削除
AndroidBackground Process高い見えるActivityのないバックグラウンドプロセス
AndroidForeground Service低い通知ありのサービス — めったに終了される
AndroidForeground Process最低アクティブなActivity — 最後に終了

アプリケーションのコールドスタートとホットスタート

コールドスタートは、アプリケーションがNot Runningから直接Activeに遷移するときに発生します。システムは新しいプロセスを作成し、クラスをロードし、スタティックフィールドを初期化し、メインスレッドを作成し、UIフレームワークを起動します。iOSでは、application(_:didFinishLaunchingWithOptions:)を呼び出し、AndroidではApplication.onCreate()とActivity.onCreate()を呼び出します。コールドスタートの所要時間は、アプリケーションの複雑さに応じて200 msから数秒までの範囲です。

ホットスタート(ウォームスタート) — アプリケーションがSuspended状態にあり、完全な再ロードなしで再開します。システムは最終のUIスタックをメモリから復元し、ユーザーは同じ場所から作業を継続します。 ホットスタートはコールドスタートよりもはるかに高速です。その理由は、コードの大半がすでにメモリにロードされているからです。iOSでは、ホットスタートはapplication(_:didFinishLaunchingWithOptions:)を呼び出さず、applicationWillEnterForegroundとapplicationDidBecomeActiveのみを呼び出します。

コールドスタートとホットスタートの違いは、ユーザーエクスペリエンスにとって重要です。コールドスタート中は、アプリケーションの起動ができるだけ高速に行われるように、開発者が確実にする必要があります。モジュールの遅延初期化、重いリソースの遅延ロード、起動時のメインスレッドでの作業を最小限にすることなどがあります。 Googleはコールドスタートを500 ms以下、AppleはiOSで400 ms以下を推奨しています。

kotlin
// Androidでのコールドスタート時間の測定
class App : Application() {
    private var startTime: Long = 0L

    override fun onCreate() {
        super.onCreate()
        startTime = System.currentTimeMillis()
    }

    fun getStartupTime(): Long {
        return System.currentTimeMillis() - startTime
    }
}

// 遅延初期化でActivityを起動
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 最初のフレームに必要な最小限のみ
        setupNavigation()
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        // レンダリング後の重い初期化
        initializeHeavyModules()
    }
}

例は、Androidでのコールドスタート時間の測定を示しています。 Application.onCreate()はNot RunningからActiveへの遷移時に呼び出されます。タイムスタンプはプロセス起動時に記録されます。Activityは、最初のフレームをブロックすることを避けるために、lazyデリゲートを介して遅延初期化を使用します。onPostCreateは、UIがすでにレンダリングされているため、重いモジュールを初期化するのに最適な場所です。

iOSでのNot Running: SwiftとAppDelegate

iOSでは、Not RunningはUIApplicationDelegateプロトコルを通して管理されます。主なメソッドは次のとおりです。application(_:didFinishLaunchingWithOptions:)はコールドスタート後に呼び出され、applicationWillTerminate(_:)はユーザーがアプリケーションを終了する前に呼び出されます。しかし、システムはapplicationWillTerminateを呼び出さずにアプリケーションを終了することがあります — 例えば、緊急終了やメモリ足りない場合です。iOSはこのメソッドが呼び出されることを保証しないため、データはapplicationDidEnterBackgroundで保存する必要があります。

iOSでNot Runningへの遷移シナリオ

ユーザーはApp Switcherでスワイプすることで、マニュアルでアプリケーションを終了できます。システムは、バックグラウンドにある間にアプリケーションをメモリからアンロードできます。アプリケーションがクラッシュする可能性もあります。いずれの場合でも、起動中に作成されたすべてのオブジェクトは破壊されます。 保存されなかった状態は永遠に失われます。 iOS 13+では、状態を保存するためにNSUserActivityまたはUIApplication.stateRestorationIdentifierを介した状態復元メカニズムを使用することをおすすめします。

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // コールドスタート: アプリがNot Runningから遷移
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // 最小限のサービスのセットを初期化
        setupAnalytics()
        configureAppearance()
        return true
    }

    // アプリが終了 — マニュアルクローズのみ
    func applicationWillTerminate(
        _ application: UIApplication
    ) {
        saveCriticalData()
    }

    // バックグラウンドに入る前にデータを保存
    func applicationDidEnterBackground(
        _ application: UIApplication
    ) {
        saveApplicationState()
    }

    private func saveCriticalData() {
        UserDefaults.standard.synchronize()
    }

    private func saveApplicationState() {
        let state = ["lastScreen": "main", "timestamp": Date()]
        try? NSKeyedArchiver.archivedData(
            withRootObject: state,
            requiringSecureCoding: true
        )
    }
}

このコードは、iOSでのNot Runningの正しい処理を示しています。 applicationWillTerminateは、ユーザーがマニュアルでアプリを終了した場合にのみ呼び出されます。重要なデータの保存はapplicationDidEnterBackgroundで二重に行われます。このメソッドは、バックグラウンドに行く前に呼び出されることが保証されているからです。状態復元により、コールドスタート中に後で復元できるようにUIスタックを保存できます。

AndroidでのNot Running: Kotlinとプロセス

Androidでは、Not Runningはアプリケーションのプロセスが存在しないことを意味します。Androidの基盤となるLinuxシステムは、Zygoteメカニズムを通してプロセスを管理します。アプリケーションが起動すると、Zygoteは新しいプロセスをフォークし、Dalvik/ARTをロードし、Application.onCreate()を呼び出します。AndroidにはapplicationWillTerminateの直接の似役はありません — システムは予告なしに何時でもプロセスを終了できます。

Androidのプロセスライフサイクル

Activityが初めて呼び出されると、システムはチェーンonCreate → onStart → onResumeを通して、プロセス、Application、Activityを作成します。ユーザーがBackを押すと、Activityは破壊され(onDestroy)、システムによってプロセスが終了される可能性があります。 iOSとの主な違いは、Androidでは、Foreground Serviceが実行中だったり、アクティブなBroadcastReceiverがあったりすると、アクティブなActivityがなくてもプロセスが存続できることです。

kotlin
// ViewModelのSavedStateHandleを介したNot Runningの処理
class MainViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    companion object {
        private const val KEY_LAST_SCREEN = "last_screen"
        private const val KEY_USER_DATA = "user_data"
    }

    fun saveCurrentState(screen: String, data: String) {
        savedStateHandle[KEY_LAST_SCREEN] = screen
        savedStateHandle[KEY_USER_DATA] = data
    }

    fun restoreState(): AppState? {
        val screen = savedStateHandle.get<String>(KEY_LAST_SCREEN)
        val data = savedStateHandle.get<String>(KEY_USER_DATA)
        return if (screen != null && data != null) {
            AppState(screen, data)
        } else null
    }
}

// Application — Not Running後の最初のコールバック
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initCrashReporter()
        initDependencyInjection()
    }
}

SavedStateHandleは、Not Runningへの遷移中に状態を自動的に保存し、コールドスタートで復元するAndroid Architecture Componentsのコンポーネントです。ViewModelProviderを通して作成されたViewModelは、画面の回転やActivityの破壊でも生き残ります。プロセスが終了すると、SavedStateHandleからのデータはBundleにシリアライズされ、保存されたインスタンス状態に保存されます。

Not Runningへの遷移の原因

Not Runningはいくつかの理由で発生します。ユーザーがマニュアルでアプリケーションを閉じる。システムがメモリ不足によりアプリケーションをアンロードする。アプリケーションが例外でクラッシュする。Androidでは、システムが一括アプリ更新やデバイスの再起動中にプロセスを終了することがあります。iOSは、バックグラウンドタスクがタイムアウト(通常30秒)すると、アプリケーションを終了する可能性があります。

原因iOSAndroid防止可能性
ユーザーによるマニュアルクローズApp SwitcherでスワイプRecentsからスワイプなし — ユーザーのアクション
メモリ不足メモリ警告が発灯onTrimMemory / LMK一部可能 — メモリ最適化
アプリのクラッシュNSException / シグナルUncaughtException / ANRあり — エラー処理とクラッシュレポート
バックグラウンドタスクのタイムアウトバックグラウンドタスクに30秒JobSchedulerに10分あり — 正しいタスクスケジューリング
OSの再起動applicationWillTerminateが呼ばれるBroadcast ACTION_SHUTDOWNなし — システムイベント
アプリ更新発生しない (iOSサンドボックス)APK更新時にプロセス終了なし — システム更新

Not Runningへの遷移を診断する方法

iOSでは、applicationWillTerminateとapplicationDidFinishLaunchingでコンソールログを使用します。起動ごとにUserDefaultsにフラグを追加します — 次回の起動時にフラグがない場合、アプリは不正に終了されたことになります。 Androidでは、ActivityManager.isBackgroundRestricted()を使用してアプリケーションがバックグラウンドタスクを実行できるかどうかを確認します。また、onTrimMemory(TRIM_MEMORY_COMPLETE)を監視します — これはプロセスが終了されることを示すシグナルです。

Not Runningとの仕事における最喁プラクティス

第一のルール — applicationWillTerminateやonDestroyが呼び出されると仮定しないでください。ActiveからBackgroundへの遷移ごとに、重要なデータを保存してください。単純な設定にはキーバリューストアを、構造化データにはSQLite/Roomを使用します。

第二のルール — コールドスタート時間を測定し、最適化します。遅延初期化、メインスレッドでの作業を最小限にする、リソースの事前読み込み、SplashScreen APIの使用 — これらはすべて起動時間のイメージを向上させます。 Googleは優れたUXのためにコールドスタートを200 ms未満にすることを推奨しています。

第三のルール — 状態復元を実装します。iOSでは、UIApplication.stateRestorationIdentifierとNSUserActivityを使用します。Androidでは、onSaveInstanceStateと組み合わせたViewModelのSavedStateHandleを使用します。これにより、アプリの再起動後にユーザーが同じ場所から作業を継続できます。

第四のルール — Not Running後にアプリケーションが起動されたlaunchOptionsやIntentを処理します。ディープリンク、プッシュ通知、ユニバーサルリンク — これらはすべて起動パラメータを通して伝えられます。開発者は、これらのデータを正しく抽出し、ユーザーを適切な画面にナビゲートする必要があります。

swift
// コールドスタート後のディープリンク処理
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // 通知が届いたか確認
    if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
        handleNotification(notification)
    }
    // ディープリンクを確認
    if let url = launchOptions?[.url] as? URL {
        handleDeepLink(url)
    }
    return true
}

private func handleDeepLink(_ url: URL) {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
          let screenId = components.queryItems?.first(where: { $0.name == "screen" })?.value
    else { return }
    openScreen(screenId)
}

このコードは、iOSのコールドスタートでの起動パラメータの処理を示しています。 launchOptionsには、システムがアプリケーションを起動したデータが含まれています。通知、ディープリンク、ユニバーサルリンクは、この辞書を通して伝えられます。開発者は、スムーズなユーザーエクスペリエンスを確保するために、あらゆる可能な起動シナリオを正しく処理する必要があります。

よくある質問

Not Runningへの遷移時、データはどうなりますか?

永続ストレージ(UserDefaults、Core Data、SharedPreferences、Room)に保存されたデータは保持されます。RAM内のデータ — 変数、キャッシュ、SavedStateHandleのないViewModelの状態 — は徹底的に失われます。したがって、バックグラウンドへの遷移ごとにアプリケーションの状態を保存することが極めて重要です。

iOSでコールドスタートとホットスタートをどのように区別しますか?

コールドスタート中には、application(_:didFinishLaunchingWithOptions:)が呼び出されます。ホットスタート(Suspendedからの戻り)中には、このメソッドは呼び出されません — applicationWillEnterForegroundとapplicationDidBecomeActiveのみが発火されます。コールドスタートでのみアクションを実行する必要がある場合は、didFinishLaunchingWithOptionsにフラグを設定します。

Androidアプリは、アクティブなServiceでNot Runningになりうるのですか?

はい。永続的な通知を持つForeground Serviceは、すべてのActivityが破壊されても、システムがプロセスを終了するのを防ぎます。Background Service(foregroundなしのstartService)は、システムによって何時でも停止される可能性があります。実行中のServiceはプロセスが存在することを意味し、これはもうNot Runningではありません。

シミュレータでNot Runningを再現するにはどうすればよいですか?

iOSシミュレータでは、App Switcher(Cmd+Shift+Hを何度か押し、上にスワイプ)を通してアプリを終了します。Androidエミュレータでは、adb shell am force-stop com.example.appまたはLogcatのStopボタンを使用します。その後、アプリケーションを再び起動します — これがNot Runningからのクリーンなコールドスタートになります。

Not Runningの文脈でkill-switchとは何ですか?

Kill-switchは、アプリケーションを緊急終了させるためのサーバーコマンドです。バンキングや企業向けアプリで、リモートアクセスをブロックするために使用されます。アプリがkillコマンドを受け取ると、次回のコールドスタート時にUIをブロックし、再認証を要求します。iOSでは、ブロックフラグのリモート通知を介して実装されます。

まとめ

  • Not Running — ライフサイクルの初期および最終状態、アプリはメモリにロードされず、コードを実行していない
  • コールドスタート — Not Runningからのアプリの完全再起動、すべてのコンポーネントをゼロから初期化する必要がある
  • ホットスタート — Suspendedからの戻り、didFinishLaunchingWithOptionsやApplication.onCreateを呼び出さない
  • データ保存 — Backgroundへの遷移時に実行することが極めて重要、Not Runningは何時でも発生しうるから
  • iOS — applicationWillTerminateは保証されず、状態はUserDefaultsまたは状態復元で保存
  • Android — プロセスは何時でも終了可能、ViewModelのSavedStateHandleが自動的に状態を保存
  • 起動の最適化 — 遅延初期化、メインスレッドでの作業を最小限に、高速な初期フレームのためのSplashScreen API

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください