Not Running — какво е това, начално състояние на жизнения цикъл

Автор: IT Sectr Публикувано: 2026-03-03 Време за четене: 11 мин

Not Running — начално състояние на жизнения цикъл на мобилно приложение, в което то все още не е стартирано или вече е приключило работа. Научете как системата iOS и Android управляват това състояние, кои събития водят до преход от Not Running и как правилно да обработвате стартирането и прекратяването на приложението в Swift и Kotlin.

Основни точки

  • Not Running — приложението не е заредено в паметта и не изпълнява код, това е точката на влизане и излизане в жизнения цикъл
  • Стартиране — преходът от Not Running става при докосване на иконата, чрез deep link или push известие
  • Прекратяване — потребителят затваря приложението с плъзгане, системата го разтоварва при липса на памет или настъпва краш
  • Студено стартиране — приложението стартира от нулата, всички обекти се създават наново, състоянието не се възстановява от кеша
  • Горещо стартиране — приложението е било в Suspended и се връща в Active без пълна инициализация

Not Running — какво е това състояние

Not Running — е основното състояние на жизнения цикъл на мобилно приложение, в което то не е заредено в RAM паметта на устройството и не консумира системни ресурси. В iOS и Android това състояние означава пълно отсъствие на процеси и нишки, свързани с приложението. Потребителят вижда иконата на приложението на работния плот, но самото приложение не е активно и не се намира в списъка на скорошните.

Когато потребителят докосне иконата, системата създава нов процес, зарежда изпълнимия код в паметта и инициализира всички необходими структури от данни. Този процес се нарича студено стартиране (cold start) и е най-ресурсоемък от гледна точка на време за зареждане.

Системата може да премести приложението в Not Running от всяко друго състояние. Ако приложението е във фонов режим (Background) или е спряно (Suspended), операционната система има право да го разтовари при липса на RAM памет за по-приоритетни задачи — например за активно приложение на преден план.

Разработчикът трябва да има предвид, че приложението може да бъде прекратено от системата по всяко време, когато е във фонов режим. Това означава, че всички незапазени данни могат да бъдат загубени. Поради това е критично важно да запазвате състоянието в хранилища ключ-стойност (UserDefaults, SharedPreferences) или в локална база данни при преходите от Active към Background.

Как системата определя кое приложение да разтовари

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 — прекратява се последно

Студено и горещо стартиране на приложението

Студено стартиране (cold start) настъпва, когато приложението преминава от Not Running директно в Active. Системата създава нов процес, зарежда класовете, инициализира статичните полета, създава главната нишка и стартира UI рамката. В iOS това означава извикване на application(_:didFinishLaunchingWithOptions:), в Android — извикване на Application.onCreate() и Activity.onCreate(). Времето за студено стартиране може да варира от 200 ms до няколко секунди в зависимост от сложността на приложението.

Горещо стартиране (warm start или hot start) — приложението е било в състояние Suspended и се връща към работа без пълно презареждане. Системата възстановява последния UI стек от паметта и потребителят продължава работа от същото място. Горещото стартиране е значително по-бързо от студеното, тъй като по-голямата част от кода вече е заредена в паметта. В iOS горещото стартиране не извиква application(_:didFinishLaunchingWithOptions:), само applicationWillEnterForeground и applicationDidBecomeActive.

Разликата между студено и горещо стартиране е критична за потребителското изживяване. При студено стартиране разработчикът трябва да осигури стартирането да стане възможно най-бързо — мързелива инициализация на модули, отложено зареждане на тежки ресурси, минимизиране на работата в главната нишка при стартиране. Google препоръчва студено стартиране не повече от 500 ms, Apple — не повече от 400 ms за iOS.

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 вече е рендериран.

Not Running в iOS: Swift и AppDelegate

В iOS Not Running се управлява чрез делегата UIApplicationDelegate. Ключови методи: application(_:didFinishLaunchingWithOptions:) се извиква след студено стартиране, applicationWillTerminate(_:) се извиква преди прекратяване на приложението от потребителя. Системата може да прекрати приложението без да извика applicationWillTerminate — например при аварийно прекратяване или разтоварване на паметта. iOS не гарантира извикването на този метод, затова данните трябва да се запазват в applicationDidEnterBackground.

Сценарии за преход в Not Running на iOS

Потребителят може ръчно да прекрати приложението с плъзгане в App Switcher. Системата може да разтовари приложението от паметта във фонов режим. Приложението може аварийно да прекрати (crash). Във всички случаи всички обекти, създадени по време на стартиране, се унищожават. Състоянието, което не е запазено, се губи безвъзвратно. В iOS 13+ за запазване на състояние се препоръчва използването на NSUserActivity или механизма state restoration чрез 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
        )
    }
}

Кодът показва правилната обработка на Not Running в iOS. applicationWillTerminate се извиква само при ръчно прекратяване от потребителя. Запазването на критични данни е дублирано в applicationDidEnterBackground, тъй като този метод гарантирано се извиква преди преминаване във фонов режим. State restoration позволява запазване на UI стека за последващо възстановяване при студено стартиране.

Not Running в Android: Kotlin и процес

В Android Not Running означава, че процесът на приложението не съществува. Linux системата, на която се базира Android, управлява процесите чрез механизма Zygote. При стартиране на приложението Zygote fork-ва нов процес, зарежда Dalvik/ART и извиква Application.onCreate(). В Android няма пряк аналог на applicationWillTerminate — системата може да прекрати процеса по всяко време без предупреждение.

Жизнен цикъл на Android процес

Когато Activity се извика за първи път, системата създава процес, Application и Activity чрез веригата onCreate → onStart → onResume. Ако потребителят натисне Back, Activity се унищожава (onDestroy) и процесът може да бъде прекратен от системата. Ключова разлика от iOS: в Android процесът може да продължи да съществува дори без активни Activity — например ако работи Foreground Service или има активен BroadcastReceiver.

kotlin
// Обработка на Not Running чрез SavedStateHandle в ViewModel
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 — компонент на Android Architecture Components, който автоматично запазва състоянието при преход в Not Running и го възстановява при студено стартиране. ViewModel, създаден чрез ViewModelProvider, оцелява при завъртане на екрана и унищожаване на Activity. При прекратяване на процеса данните от SavedStateHandle се сериализират в Bundle и се запазват в saved instance state.

Причини за преход в Not Running

Not Running настъпва по няколко причини. Потребителят ръчно затваря приложението. Системата разтоварва приложението при липса на памет. Приложението аварийно прекратява с изключение. В Android системата може да прекрати процеса при масово обновяване на приложения или рестартиране на устройството. iOS може да прекрати приложението при изтичане на времето за фонова задача (обикновено 30 секунди).

ПричинаiOSAndroidВъзможност за предотвратяване
Ръчно затваряне от потребителяПлъзгане в App SwitcherПлъзгане от RecentsНе — действие на потребителя
Липса на паметЗадействане на memory warningonTrimMemory / LMKЧастично — оптимизация на паметта
Краш на приложениетоNSException / сигналUncaughtException / ANRДа — обработка на грешки и crash-reporting
Изтичане на време за фонова задача30 сек за Background task10 мин за JobSchedulerДа — правилно планиране на задачи
Рестартиране на ОСИзвикване на applicationWillTerminateBroadcast ACTION_SHUTDOWNНе — системно събитие
Обновяване на приложениетоНе се случва (iOS Sandbox)Процесът се прекратява при 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 препоръчва студено стартиране под 200 ms за отлично UX.

Трето правило — имплементирайте State Restoration. В iOS използвайте UIApplication.stateRestorationIdentifier и NSUserActivity. В Android използвайте SavedStateHandle в ViewModel в комбинация с onSaveInstanceState. Това ще позволи на потребителя да продължи работа от същото място след рестартиране на приложението.

Четвърто правило — обработвайте launchOptions и Intent, с които приложението е стартирано след Not Running. Deep link-ове, push известия, универсални връзки — всички те се предават чрез параметрите за стартиране. Разработчикът трябва правилно да извлече тези данни и да насочи потребителя към съответния екран.

swift
// Обработка на deep link след студено стартиране
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // Проверка дали е пристигнало известие
    if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
        handleNotification(notification)
    }
    // Проверка на deep link
    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 съдържа данните, с които системата е стартирала приложението. Известията, deep link-овете и универсалните връзки се предават чрез този речник. Разработчикът трябва правилно да обработи всички възможни сценарии за стартиране, за да осигури безпроблемно потребителско изживяване.

Често задавани въпроси

Какво се случва с данните при преход в Not Running?

Данните, които са запазени в постоянно хранилище (UserDefaults, Core Data, SharedPreferences, Room), се запазват. Данните в RAM паметта — променливи, кеш, състояние на ViewModel без SavedStateHandle — се губят безвъзвратно. Поради това е критично важно да запазвате състоянието на приложението при всеки преход във фонов режим.

Как да различите студено стартиране от горещо на iOS?

При студено стартиране се извиква application(_:didFinishLaunchingWithOptions:). При горещо стартиране (връщане от Suspended) този метод не се извиква — активират се само applicationWillEnterForeground и applicationDidBecomeActive. Ако трябва да изпълните действие само при студено стартиране, задайте флаг в didFinishLaunchingWithOptions.

Може ли Android приложение да бъде в Not Running с активен Service?

Да. Foreground Service с постоянно известие предотвратява прекратяването на процеса от системата, дори ако всички Activity са унищожени. Background Service (startService без foreground) може да бъде спрян от системата по всяко време. Работещ Service означава, че процесът съществува и това вече не е Not Running.

Как да емулирате Not Running на симулатор?

На iOS симулатора прекратете приложението чрез App Switcher (Cmd+Shift+H два пъти, плъзнете нагоре). На Android емулатора използвайте adb shell am force-stop com.example.app или бутона Stop в Logcat. След това стартирайте приложението отново — това ще бъде чисто студено стартиране от Not Running.

Какво е kill-switch в контекста на Not Running?

Kill-switch — сървърна команда за аварийно прекратяване на приложението. Използва се в банкови и корпоративни приложения за отдалечено блокиране на достъпа. Ако приложението е получило kill команда, при следващото студено стартиране блокира UI и изисква повторна авторизация. В iOS kill-switch се имплементира чрез remote notifications с флаг за блокиране.

Резюме

  • Not Running — начално и крайно състояние на жизнения цикъл, приложението не е заредено в паметта и не изпълнява код
  • Студено стартиране — пълно презареждане на приложението от Not Running, изисква инициализация на всички компоненти от нулата
  • Горещо стартиране — връщане от Suspended, не извиква didFinishLaunchingWithOptions или Application.onCreate
  • Запазване на данни — критично важно при преход към Background, тъй като Not Running може да настъпи по всяко време
  • iOS — applicationWillTerminate не е гарантиран, състоянието се запазва чрез UserDefaults или state restoration
  • Android — процесът може да бъде прекратен по всяко време, SavedStateHandle в ViewModel запазва състоянието автоматично
  • Оптимизация на стартиране — мързелива инициализация, минимална работа в main thread, SplashScreen API за бърз първи кадър

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също