Not Running — o estado inicial do ciclo de vida de um aplicativo móvel quando ainda não foi iniciado ou já foi encerrado. Saiba como o iOS e o Android gerenciam esse estado, quais eventos levam à transição do Not Running e como lidar corretamente com a inicialização e o encerramento do aplicativo em Swift e Kotlin.
Pontos principais
Not Running é o estado básico do ciclo de vida de um aplicativo móvel no qual ele não está carregado na RAM do dispositivo e não consome recursos do sistema. No iOS e Android, esse estado significa a ausência completa de processos e threads associados ao aplicativo. O usuário vê o ícone do aplicativo na tela inicial, mas o próprio aplicativo não está ativo e não está na lista de aplicativos recentes.
Quando o usuário toca no ícone do aplicativo, o sistema cria um novo processo, carrega o código executável na memória e inicializa todas as estruturas de dados necessárias. Esse processo é chamado de inicialização a frio (cold start) e é o que mais consome recursos em termos de tempo de carregamento.
O sistema pode mover o aplicativo para Not Running a partir de qualquer outro estado. Se o aplicativo estiver em segundo plano ou suspenso, o sistema operacional tem o direito de descarregá-lo quando não houver RAM suficiente para tarefas de maior prioridade — por exemplo, para um aplicativo ativo em primeiro plano.
O desenvolvedor deve considerar que o aplicativo pode ser encerrado pelo sistema a qualquer momento quando estiver em segundo plano. Isso significa que todos os dados não salvos podem ser perdidos. Portanto, é de vital importância salvar o estado em armazenamentos chave-valor (UserDefaults, SharedPreferences) ou em um banco de dados local durante as transições de Active para Background.
O iOS usa prioridades com base no estado atual do aplicativo: Active tem a maior prioridade, seguido por Inactive, Background, Suspended e, finalmente, Not Running — a menor prioridade. O Android usa uma hierarquia de processos semelhante: o processo em primeiro plano tem prioridade OOM_ADJ = 0, processo Visible = 100, processo Service = 200, processo Background = 300, processo Empty = 400. Quanto maior o valor, maior a probabilidade de o processo ser encerrado quando a memória estiver baixa.
| Plataforma | Estado | Prioridade de descarga | Descrição |
|---|---|---|---|
| iOS | Not Running | Mais alta | Aplicativo não carregado — não consome recursos do sistema |
| iOS | Suspended | Alta | Aplicativo na memória mas sem executar código — primeiro alvo de descarga |
| iOS | Background | Média | Aplicativo executando tarefa em segundo plano — descarregado após tempo limite |
| iOS | Active | Baixa | Aplicativo ativo — descarregado apenas sob pressão crítica de memória |
| Android | Empty Process | Mais alta | Processo sem componentes ativos — removido primeiro |
| Android | Background Process | Alta | Processo em segundo plano sem Activity visível |
| Android | Foreground Service | Baixa | Serviço com notificação — raramente encerrado |
| Android | Foreground Process | Mínima | Activity ativo — encerrado por último |
Inicialização a frio (cold start) ocorre quando o aplicativo transita de Not Running diretamente para Active. O sistema cria um novo processo, carrega as classes, inicializa os campos estáticos, cria a thread principal e inicia o framework de UI. No iOS, isso significa chamar application(_:didFinishLaunchingWithOptions:), no Android — chamar Application.onCreate() e Activity.onCreate(). O tempo de inicialização a frio pode variar de 200 ms a vários segundos, dependendo da complexidade do aplicativo.
Inicialização a quente (warm start ou hot start) — o aplicativo estava no estado Suspended e retoma sem um recarregamento completo. O sistema restaura a última pilha de UI da memória, e o usuário continua trabalhando do mesmo ponto. A inicialização a quente é significativamente mais rápida que a inicialização a frio porque a maior parte do código já está carregada na memória. No iOS, uma inicialização a quente não chama application(_:didFinishLaunchingWithOptions:), apenas applicationWillEnterForeground e applicationDidBecomeActive.
A diferença entre inicialização a frio e a quente é crítica para a experiência do usuário. Durante uma inicialização a frio, o desenvolvedor deve garantir que o início ocorra o mais rápido possível — inicialização preguiçosa de módulos, carregamento adiado de recursos pesados, minimização do trabalho na thread principal na inicialização. O Google recomenda uma inicialização a frio de no máximo 500 ms, a Apple — no máximo 400 ms para iOS.
// Medição do tempo de inicialização a frio no Android
class App : Application() {
private var startTime: Long = 0L
override fun onCreate() {
super.onCreate()
startTime = System.currentTimeMillis()
}
fun getStartupTime(): Long {
return System.currentTimeMillis() - startTime
}
}
// Iniciar Activity com inicialização preguiçosa
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)
// Apenas o mínimo necessário para o primeiro quadro
setupNavigation()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// Inicialização pesada após a renderização
initializeHeavyModules()
}
}O exemplo mostra a medição do tempo de inicialização a frio no Android. Application.onCreate() é chamado ao transitar de Not Running para Active. O timestamp é registrado no início do processo. O Activity usa inicialização preguiçosa por meio de um delegado lazy para evitar bloquear o primeiro quadro. onPostCreate é o local ideal para inicializar módulos pesados, pois a UI já foi renderizada.
No iOS, o Not Running é gerenciado através do protocolo UIApplicationDelegate. Métodos principais: application(_:didFinishLaunchingWithOptions:) é chamado após uma inicialização a frio, applicationWillTerminate(_:) é chamado antes do usuário encerrar o aplicativo. No entanto, o sistema pode encerrar o aplicativo sem chamar applicationWillTerminate — por exemplo, durante um encerramento de emergência ou pressão de memória. O iOS não garante que este método será chamado, portanto, os dados devem ser salvos em applicationDidEnterBackground.
O usuário pode encerrar manualmente o aplicativo com um swipe no App Switcher. O sistema pode descarregar o aplicativo da memória enquanto ele está em segundo plano. O aplicativo pode falhar. Em todos os casos, todos os objetos criados durante a inicialização são destruídos. O estado que não foi salvo é perdido para sempre. No iOS 13+, é recomendado usar NSUserActivity ou o mecanismo de restauração de estado através de UIApplication.stateRestorationIdentifier para preservar o estado.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Inicialização a frio: aplicativo transitou de Not Running
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Inicializando o conjunto mínimo de serviços
setupAnalytics()
configureAppearance()
return true
}
// Aplicativo encerra — apenas fechamento manual
func applicationWillTerminate(
_ application: UIApplication
) {
saveCriticalData()
}
// Salvando dados antes de ir para segundo plano
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
)
}
}O código demonstra o tratamento correto do Not Running no iOS. applicationWillTerminate só é chamado quando o usuário encerra manualmente o aplicativo. O salvamento de dados críticos é duplicado em applicationDidEnterBackground, pois este método tem a garantia de ser chamado antes de ir para segundo plano. A restauração de estado permite salvar a pilha de UI para recuperação posterior durante uma inicialização a frio.
No Android, Not Running significa que o processo do aplicativo não existe. O sistema Linux subjacente ao Android gerencia processos através do mecanismo Zygote. Quando um aplicativo é iniciado, o Zygote bifurca um novo processo, carrega o Dalvik/ART e chama Application.onCreate(). O Android não tem um equivalente direto do applicationWillTerminate — o sistema pode encerrar o processo a qualquer momento sem aviso.
Quando um Activity é chamado pela primeira vez, o sistema cria o processo, Application e Activity através da cadeia onCreate → onStart → onResume. Se o usuário pressionar Voltar, o Activity é destruído (onDestroy), e o processo pode ser encerrado pelo sistema. Uma diferença fundamental do iOS: no Android, o processo pode continuar existindo mesmo sem Activities ativos — por exemplo, se um Foreground Service estiver em execução ou houver um BroadcastReceiver ativo.
// Tratamento de Not Running via SavedStateHandle no 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 — o primeiro callback após Not Running
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
initCrashReporter()
initDependencyInjection()
}
}SavedStateHandle é um componente do Android Architecture Components que salva automaticamente o estado durante uma transição para Not Running e o restaura em uma inicialização a frio. Um ViewModel criado através do ViewModelProvider sobrevive à rotação da tela e à destruição do Activity. Quando o processo é encerrado, os dados do SavedStateHandle são serializados em um Bundle e salvos no estado de instância salvo.
Not Running ocorre por vários motivos. O usuário fecha manualmente o aplicativo. O sistema descarrega o aplicativo devido à baixa memória. O aplicativo falha com uma exceção. No Android, o sistema pode encerrar o processo durante uma atualização em massa de aplicativos ou reinicialização do dispositivo. O iOS pode encerrar o aplicativo quando uma tarefa em segundo plano expira (geralmente 30 segundos).
| Motivo | iOS | Android | Pode ser evitado |
|---|---|---|---|
| Fechamento manual pelo usuário | Swipe no App Switcher | Swipe dos Recents | Não — ação do usuário |
| Baixa memória | Ativação de aviso de memória | onTrimMemory / LMK | Parcialmente — otimização de memória |
| Falha do aplicativo | NSException / sinal | UncaughtException / ANR | Sim — tratamento de erros e relatório de falhas |
| Tempo limite de tarefa em segundo plano | 30 seg para tarefa em segundo plano | 10 min para JobScheduler | Sim — agendamento correto de tarefas |
| Reinicialização do SO | applicationWillTerminate chamado | Broadcast ACTION_SHUTDOWN | Não — evento do sistema |
| Atualização do aplicativo | Não ocorre (iOS Sandbox) | Processo encerrado na atualização APK | Não — atualização do sistema |
Para iOS, use o registro no console em applicationWillTerminate e applicationDidFinishLaunching. Adicione uma flag no UserDefaults a cada inicialização — se a flag estiver faltando na próxima inicialização, o aplicativo foi encerrado incorretamente. No Android, use ActivityManager.isBackgroundRestricted() para verificar se o aplicativo pode executar tarefas em segundo plano. Além disso, monitore onTrimMemory(TRIM_MEMORY_COMPLETE) — este é um sinal de que o processo será encerrado.
Primeira regra — nunca presuma que applicationWillTerminate ou onDestroy serão chamados. Salve dados criticamente importantes em cada transição de Active para Background. Use armazenamentos chave-valor para configurações simples e SQLite/Room para dados estruturados.
Segunda regra — meça o tempo de inicialização a frio e otimize-o. Inicialização preguiçosa, minimização do trabalho na thread principal, pré-carregamento de recursos, uso da API SplashScreen — tudo isso melhora a percepção do tempo de inicialização. O Google recomenda uma inicialização a frio de menos de 200 ms para uma excelente experiência do usuário.
Terceira regra — implemente a Restauração de Estado. No iOS, use UIApplication.stateRestorationIdentifier e NSUserActivity. No Android, use SavedStateHandle no ViewModel combinado com onSaveInstanceState. Isso permitirá que o usuário continue trabalhando do mesmo ponto após uma reinicialização do aplicativo.
Quarta regra — lide com launchOptions e Intent com os quais o aplicativo foi iniciado após Not Running. Deep links, notificações push, links universais — todos são passados através dos parâmetros de inicialização. O desenvolvedor deve extrair corretamente esses dados e direcionar o usuário para a tela apropriada.
// Tratamento de deep link após inicialização a frio
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Verificando se uma notificação chegou
if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
handleNotification(notification)
}
// Verificando 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)
}O código mostra o tratamento dos parâmetros de inicialização em uma inicialização a frio do iOS. launchOptions contém os dados com os quais o sistema iniciou o aplicativo. Notificações, deep links e links universais são passados através deste dicionário. O desenvolvedor deve lidar corretamente com todos os cenários possíveis de inicialização para garantir uma experiência de usuário perfeita.
Perguntas frequentes
Os dados que foram salvos no armazenamento persistente (UserDefaults, Core Data, SharedPreferences, Room) são preservados. Os dados na RAM — variáveis, cache, estado do ViewModel sem SavedStateHandle — são perdidos irreversivelmente. Portanto, é de vital importância salvar o estado do aplicativo em cada transição para Background.
Durante uma inicialização a frio, application(_:didFinishLaunchingWithOptions:) é chamado. Durante uma inicialização a quente (retorno do Suspended), este método não é chamado — apenas applicationWillEnterForeground e applicationDidBecomeActive são acionados. Se você precisar executar uma ação apenas em uma inicialização a frio, defina uma flag no didFinishLaunchingWithOptions.
Sim. Um Foreground Service com uma notificação persistente impede que o sistema encerre o processo, mesmo que todos os Activities sejam destruídos. Um Background Service (startService sem foreground) pode ser interrompido pelo sistema a qualquer momento. Um Service em execução significa que o processo existe, e isso não é mais Not Running.
No simulador iOS, encerre o aplicativo através do App Switcher (Cmd+Shift+H duas vezes, swipe para cima). No emulador Android, use adb shell am force-stop com.example.app ou o botão Stop no Logcat. Depois disso, inicie o aplicativo novamente — será uma inicialização a frio limpa a partir do Not Running.
Kill-switch é um comando de servidor para encerramento de emergência do aplicativo. É usado em aplicativos bancários e corporativos para bloqueio remoto de acesso. Se o aplicativo receber um comando kill, na próxima inicialização a frio ele bloqueia a UI e solicita reautenticação. No iOS, um kill-switch é implementado através de notificações remotas com uma flag de bloqueio.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também