Not Running — henüz başlatılmamış veya zaten çalışmasını tamamlamış bir mobil uygulamanın yaşam döngüsünün başlangıç durumu. iOS ve Android’in bu durumu nasıl yönettiğini, hangi olayların Not Running’den geçişe yol açtığını ve Swift ve Kotlin’de uygulama başlatma ve sonlandırmayı nasıl doğru şekilde ele alacağınızı öğrenin.
Ana Noktalar
Not Running, bir mobil uygulamanın cihazın RAM’ine yüklenmediği ve sistem kaynaklarını tüketmediği temel yaşam döngüsü durumudur. iOS ve Android’de bu durum, uygulamayla ilişkili işlem ve iş parçacıklarının tamamen yokluğu anlamına gelir. Kullanıcı ana ekranda uygulama simgesini görür, ancak uygulamanın kendisi aktif değildir ve son uygulamalar listesinde yer almaz.
Kullanıcı uygulama simgesine dokunduğunda, sistem yeni bir işlem oluşturur, yürütülebilir kodu belleğe yükler ve gerekli tüm veri yapılarını başlatır. Bu işleme soğuk başlatma (cold start) denir ve yükleme süresi açısından en kaynak yoğun olanıdır.
Sistem, uygulamayı başka herhangi bir durumdan Not Running’e taşıyabilir. Uygulama arka planda veya askıya alınmışsa, işletim sistemi daha yüksek öncelikli görevler için yeterli RAM olmadığında onu boşaltma hakkına sahiptir — örneğin, aktif bir ön plan uygulaması için.
Geliştirici, uygulamanın arka plandayken sistem tarafından her an sonlandırılabileceğini göz önünde bulundurmalıdır. Bu, kaydedilmemiş tüm verilerin kaybolabileceği anlamına gelir. Bu nedenle, Active’den Background’a geçişler sırasında durumu anahtar-değer depolarında (UserDefaults, SharedPreferences) veya yerel bir veritabanında kaydetmek kritik öneme sahiptir.
iOS, geçerli uygulama durumuna göre öncelikler kullanır: Active en yüksek önceliğe sahiptir, ardından Inactive, Background, Suspended ve son olarak Not Running — en düşük öncelik. Android benzer bir işlem hiyerarşisi kullanır: Ön plan işleminin önceliği OOM_ADJ = 0, Visible işlemi = 100, Service işlemi = 200, Background işlemi = 300, Empty işlemi = 400. Değer ne kadar yüksekse, bellek azaldığında işlemin sonlandırılma olasılığı o kadar yüksektir.
| Platform | Durum | Boşaltma önceliği | Açıklama |
|---|---|---|---|
| iOS | Not Running | En yüksek | Uygulama yüklenmemiş — sistem kaynağı tüketmez |
| iOS | Suspended | Yüksek | Uygulama bellekte ancak kod çalışmıyor — boşaltma için ilk hedef |
| iOS | Background | Orta | Uygulama arka plan görevi yürütüyor — zaman aşımından sonra boşaltılır |
| iOS | Active | Düşük | Aktif uygulama — yalnızca kritik bellek baskısı altında boşaltılır |
| Android | Empty Process | En yüksek | Aktif bileşeni olmayan işlem — ilk önce kaldırılır |
| Android | Background Process | Yüksek | Görünür Activity’si olmayan arka plan işlemi |
| Android | Foreground Service | Düşük | Bildirimli hizmet — nadiren sonlandırılır |
| Android | Foreground Process | Minimum | Aktif Activity — en son sonlandırılır |
Soğuk başlatma (cold start), uygulamanın Not Running’den doğrudan Active’e geçmesiyle gerçekleşir. Sistem yeni bir işlem oluşturur, sınıfları yükler, statik alanları başlatır, ana iş parçacığını oluşturur ve UI çerçevesini başlatır. iOS’te bu, application(_:didFinishLaunchingWithOptions:)’yi çağırmak, Android’de ise Application.onCreate() ve Activity.onCreate()’yi çağırmak anlamına gelir. Soğuk başlatma süresi, uygulamanın karmaşıklığına bağlı olarak 200 ms ile birkaç saniye arasında değişebilir.
Sıcak başlatma (warm start veya hot start) — uygulama Suspended durumundaydı ve tamamen yeniden yükleme olmadan devam eder. Sistem son UI yığınını bellekten geri yükler ve kullanıcı aynı yerden çalışmaya devam eder. Sıcak başlatma, soğuk başlatmadan önemli ölçüde daha hızlıdır çünkü kodun çoğu zaten belleğe yüklenmiştir. iOS’te, sıcak başlatma application(_:didFinishLaunchingWithOptions:)’yi çağırmaz, yalnızca applicationWillEnterForeground ve applicationDidBecomeActive’i çağırır.
Soğuk ve sıcak başlatma arasındaki fark, kullanıcı deneyimi için kritiktir. Soğuk başlatma sırasında geliştirici, başlatmanın mümkün olduğunca hızlı gerçekleşmesini sağlamalıdır — modüllerin tembel başlatılması, ağır kaynakların gecikmeli yüklenmesi, başlatmada ana iş parçacığındaki çalışmanın en aza indirilmesi. Google, soğuk başlatmanın 500 ms’yi, Apple ise iOS için 400 ms’yi geçmemesini önerir.
// Android’de soğuk başlatma süresini ölçme
class App : Application() {
private var startTime: Long = 0L
override fun onCreate() {
super.onCreate()
startTime = System.currentTimeMillis()
}
fun getStartupTime(): Long {
return System.currentTimeMillis() - startTime
}
}
// Tembel başlatma ile Activity başlatma
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)
// Yalnızca ilk kare için gerekli minimum
setupNavigation()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// Oluşturmadan sonra ağır başlatma
initializeHeavyModules()
}
}Örnek, Android’de soğuk başlatma süresinin ölçümünü göstermektedir. Application.onCreate(), Not Running’den Active’e geçişte çağrılır. Zaman damgası, işlem başlatıldığında kaydedilir. Activity, ilk kareyi engellememek için bir tembel temsilci aracılığıyla tembel başlatma kullanır. onPostCreate, ağır modülleri başlatmak için en uygun yerdir çünkü UI zaten oluşturulmuştur.
iOS’te Not Running, UIApplicationDelegate protokolü aracılığıyla yönetilir. Anahtar yöntemler: application(_:didFinishLaunchingWithOptions:) soğuk başlatmadan sonra çağrılır, applicationWillTerminate(_:) kullanıcının uygulamayı sonlandırmasından önce çağrılır. Bununla birlikte, sistem applicationWillTerminate’i çağırmadan uygulamayı sonlandırabilir — örneğin, acil bir sonlandırma veya bellek baskısı sırasında. iOS, bu yöntemin çağrılacağını garanti etmez, bu nedenle veriler applicationDidEnterBackground’da kaydedilmelidir.
Kullanıcı, App Switcher’da kaydırarak uygulamayı manuel olarak sonlandırabilir. Sistem, arka plandayken uygulamayı bellekten boşaltabilir. Uygulama çökebilir. Tüm durumlarda, başlatma sırasında oluşturulan tüm nesneler yok edilir. Kaydedilmeyen durum sonsuza dek kaybolur. iOS 13+ sürümünde, durumu korumak için NSUserActivity veya UIApplication.stateRestorationIdentifier aracılığıyla durum geri yükleme mekanizmasının kullanılması önerilir.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Soğuk başlatma: uygulama Not Running’den geçti
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Minimum hizmet setini başlatma
setupAnalytics()
configureAppearance()
return true
}
// Uygulama sonlanıyor — yalnızca manuel kapatma
func applicationWillTerminate(
_ application: UIApplication
) {
saveCriticalData()
}
// Arka plana geçmeden önce verileri kaydetme
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
)
}
}Kod, iOS’te Not Running’in doğru şekilde ele alınmasını göstermektedir. applicationWillTerminate yalnızca kullanıcı uygulamayı manuel olarak sonlandırdığında çağrılır. Kritik verilerin kaydedilmesi applicationDidEnterBackground’da tekrarlanır çünkü bu yöntemin arka plana geçmeden önce çağrılacağı garanti edilir. Durum geri yükleme, soğuk başlatma sırasında daha sonra kurtarma için UI yığınını kaydetmeye olanak tanır.
Android’de Not Running, uygulama işleminin mevcut olmadığı anlamına gelir. Android’in temelini oluşturan Linux sistemi, işlemleri Zygote mekanizması aracılığıyla yönetir. Bir uygulama başlatıldığında, Zygote yeni bir işlem çatalı oluşturur, Dalvik/ART’yi yükler ve Application.onCreate()’yi çağırır. Android’in applicationWillTerminate’in doğrudan bir karşılığı yoktur — sistem, uyarı vermeden işlemi herhangi bir zamanda sonlandırabilir.
Bir Activity ilk kez çağrıldığında, sistem onCreate → onStart → onResume zinciri aracılığıyla işlemi, Application’ı ve Activity’yi oluşturur. Kullanıcı Geri tuşuna basarsa, Activity yok edilir (onDestroy) ve işlem sistem tarafından sonlandırılabilir. iOS’ten önemli bir fark: Android’de işlem, aktif Activity’ler olmasa bile varlığını sürdürebilir — örneğin, bir Foreground Service çalışıyorsa veya aktif bir BroadcastReceiver varsa.
// ViewModel’de SavedStateHandle aracılığıyla Not Running’i ele alma
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’den sonraki ilk geri çağrı
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
initCrashReporter()
initDependencyInjection()
}
}SavedStateHandle, Not Running’e geçiş sırasında durumu otomatik olarak kaydeden ve soğuk başlatmada geri yükleyen bir Android Architecture Components bileşenidir. ViewModelProvider aracılığıyla oluşturulan bir ViewModel, ekran döndürme ve Activity yok edilmesinden sağ çıkar. İşlem sonlandırıldığında, SavedStateHandle’daki veriler bir Bundle içinde serileştirilir ve kaydedilmiş örnek durumunda saklanır.
Not Running birkaç nedenden dolayı gerçekleşir. Kullanıcı uygulamayı manuel olarak kapatır. Sistem, bellek yetersizliği nedeniyle uygulamayı boşaltır. Uygulama bir istisna ile çöker. Android’de sistem, toplu uygulama güncellemesi veya cihaz yeniden başlatması sırasında işlemi sonlandırabilir. iOS, bir arka plan görevinin süresi dolduğunda (genellikle 30 saniye) uygulamayı sonlandırabilir.
| Neden | iOS | Android | Önlenebilir mi |
|---|---|---|---|
| Kullanıcı tarafından manuel kapatma | App Switcher’da kaydırma | Recents’ten kaydırma | Hayır — kullanıcı eylemi |
| Bellek yetersizliği | Bellek uyarısı tetiklenmesi | onTrimMemory / LMK | Kısmen — bellek optimizasyonu |
| Uygulama çökmesi | NSException / sinyal | UncaughtException / ANR | Evet — hata yönetimi ve çökme raporlaması |
| Arka plan görevi zaman aşımı | Arka plan görevi için 30 saniye | JobScheduler için 10 dakika | Evet — doğru görev planlaması |
| İşletim sistemi yeniden başlatması | applicationWillTerminate çağrılır | Broadcast ACTION_SHUTDOWN | Hayır — sistem olayı |
| Uygulama güncellemesi | Gerçekleşmez (iOS Sandbox) | APK güncellemesinde işlem sonlandırılır | Hayır — sistem güncellemesi |
iOS için, applicationWillTerminate ve applicationDidFinishLaunching’de konsol günlüğü kullanın. Her başlatmada UserDefaults’a bir işaret ekleyin — işaret bir sonraki başlatmada eksikse, uygulama hatalı bir şekilde sonlandırılmıştır. Android’de, uygulamanın arka plan görevlerini çalıştırıp çalıştıramayacağını kontrol etmek için ActivityManager.isBackgroundRestricted()’yi kullanın. Ayrıca, onTrimMemory(TRIM_MEMORY_COMPLETE)’yi izleyin — bu, işlemin sonlandırılacağına dair bir sinyaldir.
İlk kural — applicationWillTerminate veya onDestroy’nin çağrılacağını asla varsaymayın. Active’den Background’a her geçişte kritik derecede önemli verileri kaydedin. Basit ayarlar için anahtar-değer depolarını ve yapılandırılmış veriler için SQLite/Room’u kullanın.
İkinci kural — soğuk başlatma süresini ölçün ve optimize edin. Tembel başlatma, ana iş parçacığındaki çalışmayı en aza indirme, kaynakları önceden yükleme, SplashScreen API’sini kullanma — bunların tümü algılanan başlatma süresini iyileştirir. Google, mükemmel bir kullanıcı deneyimi için soğuk başlatmanın 200 ms’nin altında olmasını önerir.
Üçüncü kural — Durum Geri Yüklemeyi uygulayın. iOS’te UIApplication.stateRestorationIdentifier ve NSUserActivity’yi kullanın. Android’de, onSaveInstanceState ile birleştirilmiş ViewModel’de SavedStateHandle’ı kullanın. Bu, kullanıcının uygulama yeniden başlatıldıktan sonra aynı yerden çalışmaya devam etmesini sağlar.
Dördüncü kural — uygulamanın Not Running’den sonra başlatıldığı launchOptions ve Intent’i ele alın. Derin bağlantılar, push bildirimleri, evrensel bağlantılar — bunların tümü başlatma parametreleri aracılığıyla iletilir. Geliştirici, bu verileri doğru bir şekilde çıkarmalı ve kullanıcıyı ilgili ekrana yönlendirmelidir.
// Soğuk başlatmadan sonra derin bağlantıyı ele alma
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Bildirimin gelip gelmediğini kontrol etme
if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
handleNotification(notification)
}
// Derin bağlantıyı kontrol etme
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)
}Kod, iOS soğuk başlatmasında başlatma parametrelerinin ele alınmasını göstermektedir. launchOptions, sistemin uygulamayı başlattığı verileri içerir. Bildirimler, derin bağlantılar ve evrensel bağlantılar bu sözlük aracılığıyla iletilir. Geliştirici, kesintisiz bir kullanıcı deneyimi sağlamak için tüm olası başlatma senaryolarını doğru bir şekilde ele almalıdır.
Sıkça Sorulan Sorular
Kalıcı depolamada (UserDefaults, Core Data, SharedPreferences, Room) kaydedilen veriler korunur. RAM’deki veriler — değişkenler, önbellek, SavedStateHandle olmayan ViewModel durumu — geri dönülmez bir şekilde kaybolur. Bu nedenle, her Background’a geçişte uygulama durumunu kaydetmek kritik öneme sahiptir.
Soğuk başlatma sırasında application(_:didFinishLaunchingWithOptions:) çağrılır. Sıcak başlatma (Suspended’dan dönüş) sırasında bu yöntem çağrılmaz — yalnızca applicationWillEnterForeground ve applicationDidBecomeActive tetiklenir. Yalnızca soğuk başlatmada bir eylem gerçekleştirmeniz gerekiyorsa, didFinishLaunchingWithOptions’da bir işaret ayarlayın.
Evet. Kalıcı bildirime sahip bir Foreground Service, tüm Activities yok edilse bile sistemin işlemi sonlandırmasını engeller. Bir Background Service (foreground olmadan startService), sistem tarafından herhangi bir zamanda durdurulabilir. Çalışan bir Service, işlemin var olduğu anlamına gelir ve bu artık Not Running değildir.
iOS simülatöründe, uygulamayı App Switcher (Cmd+Shift+H iki kez, yukarı kaydırma) aracılığıyla sonlandırın. Android emülatöründe, adb shell am force-stop com.example.app veya Logcat’teki Stop düğmesini kullanın. Bundan sonra uygulamayı tekrar başlatın — bu, Not Running’den temiz bir soğuk başlatma olacaktır.
Kill-switch, acil uygulama sonlandırması için bir sunucu komutudur. Bankacılık ve kurumsal uygulamalarda uzaktan erişim engellemesi için kullanılır. Uygulama bir kill komutu alırsa, bir sonraki soğuk başlatmada UI’yı bloke eder ve yeniden kimlik doğrulaması ister. iOS’te kill-switch, engelleme işaretli uzak bildirimler aracılığıyla uygulanır.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun