Not Running — de begintoestand van de levenscyclus van een mobiele app, waarin deze nog niet is gestart of al is beëindigd. Ontdek hoe het iOS- en Android-systeem deze toestand beheren, welke gebeurtenissen leiden tot de overgang van Not Running en hoe u het starten en beëindigen van de app correct verwerkt in Swift en Kotlin.
Belangrijkste punten
Not Running — is de basistoestand van de levenscyclus van een mobiele app, waarin deze niet in het RAM-geheugen van het apparaat is geladen en geen systeembronnen verbruikt. In iOS en Android betekent deze toestand de volledige afwezigheid van processen en threads die aan de app zijn gekoppeld. De gebruiker ziet het app-pictogram op het bureaublad, maar de app zelf is niet actief en staat niet in de recentenlijst.
Wanneer de gebruiker op het pictogram tikt, maakt het systeem een nieuw proces aan, laadt de uitvoerbare code in het geheugen en initialiseert alle benodigde gegevensstructuren. Dit proces wordt een koude start (cold start) genoemd en is het meest bronnenintensief wat betreft laadtijd.
Het systeem kan de app vanuit elke andere toestand naar Not Running verplaatsen. Als de app zich op de achtergrond (Background) of in geschorste toestand (Suspended) bevindt, heeft het besturingssysteem het recht deze te verwijderen bij gebrek aan RAM-geheugen voor prioritaire taken — bijvoorbeeld voor een actieve app op de voorgrond.
De ontwikkelaar moet er rekening mee houden dat de app op elk moment door het systeem kan worden beëindigd wanneer deze zich op de achtergrond bevindt. Dit betekent dat alle niet-opgeslagen gegevens verloren kunnen gaan. Daarom is het van cruciaal belang om de toestand op te slaan in sleutel-waarde-opslagplaatsen (UserDefaults, SharedPreferences) of in een lokale database bij overgangen van Active naar Background.
iOS gebruikt prioriteiten op basis van de huidige toestand van de app: Active heeft de hoogste prioriteit, daarna Inactive, Background, Suspended en ten slotte Not Running — minimale prioriteit. Android gebruikt een vergelijkbare proceshiërarchie: het Foreground-proces heeft prioriteit OOM_ADJ = 0, Visible-proces = 100, Service-proces = 200, Background-proces = 300, Empty-proces = 400. Hoe hoger de waarde, hoe groter de kans dat het proces wordt beëindigd bij geheugengebrek.
| Platform | Toestand | Verwijderprioriteit | Beschrijving |
|---|---|---|---|
| iOS | Not Running | Hoogste | App niet geladen — systeembron wordt niet verbruikt |
| iOS | Suspended | Hoog | App in geheugen, maar code wordt niet uitgevoerd — eerste doel voor verwijdering |
| iOS | Background | Gemiddeld | App voert achtergrondtaak uit — verwijderd na timeout |
| iOS | Active | Laag | Actieve app — alleen verwijderd bij kritiek geheugengebrek |
| Android | Empty Process | Hoogste | Proces zonder actieve componenten — eerst verwijderd |
| Android | Background Process | Hoog | Achtergrondproces zonder zichtbare Activity |
| Android | Foreground Service | Laag | Service met melding — zelden beëindigd |
| Android | Foreground Process | Minimaal | Actieve Activity — als laatste beëindigd |
Koude start (cold start) vindt plaats wanneer de app van Not Running direct naar Active overgaat. Het systeem maakt een nieuw proces aan, laadt klassen, initialiseert statische velden, maakt de hoofdthread aan en start het UI-framework. In iOS betekent dit het aanroepen van application(_:didFinishLaunchingWithOptions:), in Android — het aanroepen van Application.onCreate() en Activity.onCreate(). De tijd van een koude start kan variëren van 200 ms tot enkele seconden, afhankelijk van de complexiteit van de app.
Warme start (warm start of hot start) — de app was in de toestand Suspended en keert terug naar werking zonder volledige herlading. Het systeem herstelt de laatste UI-stack uit het geheugen en de gebruiker gaat verder waar hij gebleven was. Een warme start is aanzienlijk sneller dan een koude start, omdat het grootste deel van de code al in het geheugen is geladen. In iOS roept een warme start application(_:didFinishLaunchingWithOptions:) niet aan, alleen applicationWillEnterForeground en applicationDidBecomeActive.
Het verschil tussen koude en warme start is kritisch voor de gebruikerservaring. Bij een koude start moet de ontwikkelaar ervoor zorgen dat het opstarten zo snel mogelijk gebeurt — luie initialisatie van modules, uitgesteld laden van zware bronnen, minimalisatie van werk in de hoofdthread bij het opstarten. Google beveelt aan een koude start van niet meer dan 500 ms, Apple — niet meer dan 400 ms voor iOS.
// Meten van koude starttijd in 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 starten met luie initialisatie
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)
// Alleen het absolute minimum voor het eerste frame
setupNavigation()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// Zware initialisatie na rendering
initializeHeavyModules()
}
}Het voorbeeld toont het meten van de koude starttijd in Android. Application.onCreate() wordt aangeroepen bij de overgang van Not Running naar Active. De tijdstempel wordt geregistreerd bij het starten van het proces. Activity gebruikt luie initialisatie via een lazy-delegate om het eerste frame niet te blokkeren. onPostCreate is de optimale plaats voor initialisatie van zware modules, omdat de UI al is weergegeven.
In iOS wordt Not Running beheerd via de delegate UIApplicationDelegate. Belangrijke methoden: application(_:didFinishLaunchingWithOptions:) wordt aangeroepen na een koude start, applicationWillTerminate(_:) wordt aangeroepen voor beëindiging van de app door de gebruiker. Het systeem kan de app echter beëindigen zonder applicationWillTerminate aan te roepen — bijvoorbeeld bij noodbeëindiging of geheugenverwijdering. iOS garandeert niet dat deze methode wordt aangeroepen, daarom moeten gegevens worden opgeslagen in applicationDidEnterBackground.
De gebruiker kan de app handmatig beëindigen met een veegbeweging in de App Switcher. Het systeem kan de app uit het geheugen verwijderen op de achtergrond. De app kan noodgedwongen worden beëindigd (crash). In alle gevallen worden alle objecten die tijdens het opstarten zijn gemaakt, vernietigd. Toestand die niet is opgeslagen, gaat onherroepelijk verloren. In iOS 13+ wordt voor het opslaan van de toestand het gebruik van NSUserActivity of het mechanisme voor state restoration via UIApplication.stateRestorationIdentifier aanbevolen.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Koude start: app overgegaan van Not Running
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Initialisatie van minimale set services
setupAnalytics()
configureAppearance()
return true
}
// App beëindigt — alleen handmatig sluiten
func applicationWillTerminate(
_ application: UIApplication
) {
saveCriticalData()
}
// Gegevens opslaan voor naar achtergrond gaan
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
)
}
}De code toont de correcte verwerking van Not Running in iOS. applicationWillTerminate wordt alleen aangeroepen bij handmatige beëindiging door de gebruiker. Het opslaan van kritieke gegevens wordt gedupliceerd in applicationDidEnterBackground, omdat deze methode gegarandeerd wordt aangeroepen voor het naar de achtergrond gaan. State restoration maakt het mogelijk de UI-stack op te slaan voor later herstel bij een koude start.
In Android betekent Not Running dat het proces van de app niet bestaat. Het Linux-systeem waarop Android is gebaseerd, beheert processen via het Zygote-mechanisme. Bij het starten van de app fork Zygote een nieuw proces, laadt Dalvik/ART en roept Application.onCreate() aan. In Android bestaat geen directe analoog van applicationWillTerminate — het systeem kan het proces op elk moment zonder waarschuwing beëindigen.
Wanneer Activity voor het eerst wordt aangeroepen, maakt het systeem het proces, Application en Activity aan via de keten onCreate → onStart → onResume. Als de gebruiker op Back drukt, wordt Activity vernietigd (onDestroy) en kan het proces door het systeem worden beëindigd. Belangrijk verschil met iOS: in Android kan het proces blijven bestaan, zelfs zonder actieve Activities — bijvoorbeeld als er een Foreground Service draait of een actieve BroadcastReceiver is.
// Verwerking van Not Running via SavedStateHandle in 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 — eerste callback na Not Running
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
initCrashReporter()
initDependencyInjection()
}
}SavedStateHandle — een component van Android Architecture Components die automatisch de toestand opslaat bij overgang naar Not Running en deze herstelt bij een koude start. ViewModel, gemaakt via ViewModelProvider, overleeft schermrotatie en vernietiging van Activity. Bij beëindiging van het proces worden gegevens uit SavedStateHandle geserialiseerd naar Bundle en opgeslagen in saved instance state.
Not Running treedt om verschillende redenen op. De gebruiker sluit de app handmatig. Het systeem verwijdert de app bij geheugengebrek. De app wordt noodgedwongen beëindigd met een uitzondering. In Android kan het systeem het proces beëindigen bij massa-update van apps of herstart van het apparaat. iOS kan de app beëindigen bij het verlopen van de tijd van een achtergrondtaak (meestal 30 seconden).
| Reden | iOS | Android | Mogelijkheid tot voorkomen |
|---|---|---|---|
| Handmatig sluiten door gebruiker | Veeg in App Switcher | Veeg uit Recents | Nee — gebruikersactie |
| Geheugengebrek | Activeren memory warning | onTrimMemory / LMK | Gedeeltelijk — geheugenoptimalisatie |
| Crash van app | NSException / signaal | UncaughtException / ANR | Ja — foutafhandeling en crash-reporting |
| Timeout achtergrondtaak | 30 sec voor Background task | 10 min voor JobScheduler | Ja — correcte taakplanning |
| Herstart van OS | Aanroep applicationWillTerminate | Broadcast ACTION_SHUTDOWN | Nee — systeemgebeurtenis |
| Update van app | Gebeurt niet (iOS Sandbox) | Proces wordt beëindigd bij APK-update | Nee — systeemupdate |
Gebruik voor iOS console-logging in applicationWillTerminate en applicationDidFinishLaunching. Voeg bij elke start een vlag toe aan UserDefaults — als de vlag bij de volgende start ontbreekt, is de app niet correct beëindigd. Gebruik in Android ActivityManager.isBackgroundRestricted() om te controleren of de app achtergrondtaken kan uitvoeren. Houd ook onTrimMemory(TRIM_MEMORY_COMPLETE) in de gaten — dit is een signaal dat het proces zal worden beëindigd.
Eerste regel — ga er nooit van uit dat applicationWillTerminate of onDestroy zullen worden aangeroepen. Sla kritiek belangrijke gegevens op bij elke overgang van Active naar Background. Gebruik sleutel-waarde-opslagplaatsen voor eenvoudige instellingen en SQLite/Room voor gestructureerde gegevens.
Tweede regel — meet de koude starttijd en optimaliseer deze. Luie initialisatie, minimalisatie van werk in de hoofdthread, vooraf laden van bronnen, gebruik van SplashScreen API — dit alles verbetert de perceptie van de starttijd. Google beveelt aan een koude start van minder dan 200 ms voor een uitstekende UX.
Derde regel — implementeer State Restoration. Gebruik in iOS UIApplication.stateRestorationIdentifier en NSUserActivity. Gebruik in Android SavedStateHandle in ViewModel in combinatie met onSaveInstanceState. Dit stelt de gebruiker in staat verder te gaan vanaf dezelfde plek na een herstart van de app.
Vierde regel — verwerk launchOptions en Intent waarmee de app na Not Running is gestart. Deep links, pushmeldingen, universele links — ze worden allemaal doorgegeven via de startparameters. De ontwikkelaar moet deze gegevens correct extraheren en de gebruiker naar het juiste scherm leiden.
// Verwerking van deep link na koude start
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Controleren of melding is aangekomen
if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
handleNotification(notification)
}
// Deep link controleren
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)
}De code toont de verwerking van startparameters bij een koude start in iOS. launchOptions bevat de gegevens waarmee het systeem de app heeft gestart. Meldingen, deep links en universele links worden via dit woordenboek doorgegeven. De ontwikkelaar moet alle mogelijke startscenario's correct verwerken om een naadloze gebruikerservaring te garanderen.
Veelgestelde vragen
Gegevens die in permanente opslag zijn opgeslagen (UserDefaults, Core Data, SharedPreferences, Room), blijven behouden. Gegevens in het RAM-geheugen — variabelen, cache, ViewModel-toestand zonder SavedStateHandle — gaan onherroepelijk verloren. Daarom is het van cruciaal belang om de app-toestand op te slaan bij elke overgang naar de achtergrond.
Bij een koude start wordt application(_:didFinishLaunchingWithOptions:) aangeroepen. Bij een warme start (terugkeer uit Suspended) wordt deze methode niet aangeroepen — alleen applicationWillEnterForeground en applicationDidBecomeActive worden geactiveerd. Als u een actie alleen bij een koude start wilt uitvoeren, stel dan een vlag in in didFinishLaunchingWithOptions.
Ja. Foreground Service met een permanente melding voorkomt dat het proces door het systeem wordt beëindigd, zelfs als alle Activities zijn vernietigd. Background Service (startService zonder foreground) kan op elk moment door het systeem worden gestopt. Een draaiende Service betekent dat het proces bestaat en dit is niet langer Not Running.
Beëindig op de iOS-simulator de app via de App Switcher (Cmd+Shift+H twee keer, veeg omhoog). Gebruik op de Android-emulator adb shell am force-stop com.example.app of de Stop-knop in Logcat. Start de app vervolgens opnieuw — dit is een schone koude start vanuit Not Running.
Kill-switch — een serveropdracht voor noodbeëindiging van de app. Wordt gebruikt in bank- en bedrijfsapps voor het op afstand blokkeren van toegang. Als de app een kill-opdracht heeft ontvangen, blokkeert deze bij de volgende koude start de UI en vraagt om herauthenticatie. In iOS wordt een kill-switch geïmplementeerd via remote notifications met een blokkeervlag.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook