ประเด็นสำคัญ
App Lifecycle คือเครื่องจักรสถานะจำกัดที่อธิบายสถานะที่เป็นไปได้ทั้งหมดของแอปพลิเคชันมือถือและการเปลี่ยนแปลงระหว่างสถานะเหล่านั้น แต่ละสถานะกำหนดว่าแอปสามารถเรียกใช้โค้ด แสดงส่วนติดต่อผู้ใช้ และใช้ทรัพยากรได้หรือไม่ iOS และ Android มีโมเดลที่คล้ายกันแต่ไม่เหมือนกัน — ความแตกต่างเกิดจากสถาปัตยกรรมระบบปฏิบัติการ: iOS ใช้การควบคุมหน่วยความจำที่เข้มงวด (สถานะ suspended) ในขณะที่ Android ใช้การจัดการกระบวนการที่ยืดหยุ่นผ่าน onSaveInstanceState
ใน iOS วงจรชีวิตเชื่อมโยงอย่างแน่นหนากับเบื้องหน้า/เบื้องหลัง: แอปจะทำงานหรือถูกระงับ ใน Android วงจรชีวิตมีความละเอียดมากขึ้น — Activity/หน้าต่างผ่าน 6+ สถานะ และกระบวนการเองมีวงจรชีวิตเพิ่มเติมผ่าน ProcessLifecycleOwner แอปพลิเคชันสมัยใหม่ (ตั้งแต่ปี 2024) ใช้รูปแบบสถาปัตยกรรมที่อิงตามวงจรชีวิตมากขึ้น: SwiftUI Lifecycle (iOS 16+) ผ่านสภาพแวดล้อม .scenePhase, Compose Lifecycle ผ่าน LifecycleEventObserver รูปแบบเหล่านี้จัดการการสลับบริบทโดยอัตโนมัติและป้องกันหน่วยความจำรั่วไหล ตามข้อมูลของ Google การจัดการวงจรชีวิตที่ถูกต้องใน Android ลดการใช้หน่วยความจำลง 30–40 % ระหว่างการทำงานเบื้องหลัง
ใน iOS วงจรชีวิตแอปถูกจัดการผ่านคลาส UIApplicationDelegate (AppDelegate) วิธีการหลัก: application(_:didFinishLaunchingWithOptions:) — การเริ่มต้นเมื่อเปิดครั้งแรก; applicationDidBecomeActive — แอปปรากฏและพร้อมสำหรับการโต้ตอบ; applicationWillResignActive — การเปลี่ยนไปสู่สถานะไม่ทำงาน (สายเรียกเข้า การปิดแผงการแจ้งเตือน); applicationDidEnterBackground — แอปถูกซ่อน มีประมาณ 5 วินาทีในการบันทึกข้อมูล; applicationWillEnterForeground — การกลับมาจากเบื้องหลัง; applicationWillTerminate — การสิ้นสุด (เรียกเฉพาะสำหรับแอปเก่าที่ไม่มี suspended)
เริ่มต้นจาก iOS 13 Apple ได้แนะนำ SceneDelegate (UISceneDelegate) เพื่อรองรับแอปพลิเคชันหลายหน้าต่างบน iPad และ macOS Catalyst SceneDelegate จัดการวงจรชีวิตของแต่ละฉาก (หน้าต่าง) แต่ละฉาก: scene(_:willConnectTo:options:) — การสร้างฉาก; sceneDidBecomeActive — ฉากทำงาน; sceneWillResignActive — ฉากสูญเสียโฟกัส; sceneDidEnterBackground — ฉากถูกซ่อน AppDelegate จัดการเหตุการณ์ระดับโลก (การโหลดข้อมูล การแจ้งเตือนแบบพุช) ในขณะที่ SceneDelegate จัดการสถานะส่วนติดต่อผู้ใช้ของแต่ละหน้าต่าง ใน SwiftUI วงจรชีวิตถูกจัดการผ่านค่าสภาพแวดล้อม @Environment(\.scenePhase): .active, .inactive, .background — ซึ่งให้ API ที่เป็นหนึ่งเดียวสำหรับทั้งแอป SwiftUI และ UIKit
ใน Android วงจรชีวิตเริ่มต้นด้วยคลาส Application — ซิงเกิลตันที่สร้างก่อน Activity ใด ๆ วิธี Application.onCreate() เป็นจุดเข้าสู่แรก ใช้สำหรับการเริ่มต้นระดับโลก (Analytics, DI, Crash Reporting) หลังจากนั้น Activity จะเริ่มทำงานตามลำดับ: onCreate() → onStart() → onResume() เมื่อย่อ: onPause() → onStop() → onSaveInstanceState() เมื่อถูกทำลาย: onDestroy() เมื่อกลับมา: onRestart() → onStart() → onResume()
ProcessLifecycleOwner (จาก AndroidX Lifecycle 2.2+) ติดตามวงจรชีวิตของทั้งกระบวนการ ไม่ใช่ Activity แต่ละตัว มันให้สองสถานะ: ON_RESUME (แอปอยู่ในเบื้องหน้า) และ ON_STOP (แอปถูกซ่อน) ProcessLifecycleOwner แก้ปัญหา “เมื่อใดที่แอปไปที่เบื้องหลัง” ในระดับกระบวนการ — ซึ่งสำคัญสำหรับ SDK, ไลบรารี และแอปพลิเคชันแบบโมดูลาร์ ใน Jetpack Compose วงจรชีวิตถูกจัดการผ่าน LifecycleEventObserver และ collectAsStateWithLifecycle ซึ่งระงับการรวบรวมข้อมูลโดยอัตโนมัติในโหมดเบื้องหลัง Android 14 (API 34) เพิ่มการแจ้งเตือนวงจรชีวิตบริการเบื้องหน้าผ่าน Service.onTimeout — บริการต้องเสร็จภายใน 6 ชั่วโมง มิฉะนั้นระบบจะหยุดโดยบังคับ
| iOS | Android | คำอธิบาย |
|---|---|---|
| Not Running | — | แอปยังไม่ได้เริ่มทำงาน |
| didFinishLaunching | Application.onCreate | การเริ่มต้นครั้งแรกเมื่อเปิด |
| Active | onResume | แอปปรากฏและรับอินพุต |
| Inactive | onPause | การสูญเสียโฟกัสชั่วคราว (สายเรียกเข้า แผงการแจ้งเตือน) |
| Background | onStop | แอปถูกซ่อน โค้ดอาจทำงาน |
| Suspended | — | โค้ดไม่ทำงาน หน่วยความจำถูกสงวน |
| Will Terminate | onDestroy | แอปกำลังสิ้นสุด |
ความแตกต่างหลัก: iOS บังคับให้แอปเปลี่ยนเป็น Suspended (โค้ดหยุดทำงาน) 5-10 วินาทีหลังจากเข้าสู่เบื้องหลัง Android อนุญาตให้ทำงานเบื้องหลัง (บริการ, WorkManager) ในระยะเวลาที่ยาวนานกว่า แต่มีข้อจำกัดจากโหมด Doze (Android 6+) และข้อจำกัดเบื้องหลัง (Android 12+) ProcessLifecycleOwner ใน Android เทียบเท่ากับ UIApplicationDidEnterBackgroundNotification ใน iOS โดยมีความแตกต่างที่มันทำงานเมื่อมีการเปลี่ยนไปยังเบื้องหลังทุกครั้ง ไม่ใช่แค่หลังจากสูญเสียโฟกัส
การใช้งาน AppDelegate พื้นฐานที่จัดการสถานะวงจรชีวิตทั้งหมด การบันทึกข้อมูลเมื่อเปลี่ยนไปเบื้องหลังและการอัปเดตส่วนติดต่อผู้ใช้เมื่อกลับมา
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Analytics.shared.initialize()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
func applicationWillResignActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appWillResignActive, object: nil)
}
func applicationDidEnterBackground(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
UserDefaults.standard.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
NetworkManager.shared.refreshSession()
}
}
AppDelegate เป็นจุดศูนย์กลางของวงจรชีวิต applicationDidEnterBackground เป็นวิธีการสำคัญ: iOS ให้เวลา ~5 วินาทีในการบันทึก applicationDidBecomeActive — อัปเดตส่วนติดต่อผู้ใช้ เริ่มต้นแอนิเมชันใหม่ สำหรับ SceneDelegate (iOS 13+) ตรรกะวงจรชีวิตจะกระจายไปยังฉาก: sceneDidBecomeActive / sceneDidEnterBackground จัดการหน้าต่างแต่ละหน้าต่าง
SceneDelegate สำหรับแอปพลิเคชัน iPad หลายหน้าต่าง แต่ละฉากมีวงจรชีวิตของตัวเอง เป็นอิสระจากหน้าต่างอื่น
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
func sceneDidBecomeActive(_ scene: UIScene) {
PlayerManager.shared.resume()
}
func sceneDidEnterBackground(_ scene: UIScene) {
PlayerManager.shared.pause()
CoreDataManager.shared.saveContext()
}
func sceneDidDisconnect(_ scene: UIScene) {
PlayerManager.shared.cleanup()
}
}
SceneDelegate จัดการวงจรชีวิตของฉากแต่ละฉาก sceneDidBecomeActive / sceneDidEnterBackground เป็นวิธีการที่เทียบเท่ากับ AppDelegate สำหรับหน้าต่างเฉพาะ sceneDidDisconnect จะถูกเรียกเมื่อฉากถูกปิด (ผู้ใช้ปิดหน้าต่างบน iPad) สำหรับความเข้ากันได้กับ iOS 12 และต่ำกว่า AppDelegate ต้องทำซ้ำตรรกะของ SceneDelegate ใน iOS 17+ คุณสามารถใช้ @Observable เพื่อหยุด/เริ่มงานใหม่โดยอัตโนมัติตาม scenePhase
ProcessLifecycleOwner ติดตามว่าแอปอยู่ในเบื้องหน้า/เบื้องหลังในระดับกระบวนการ นี่เป็นแนวทางที่เหมาะสมที่สุดสำหรับการจัดการวงจรชีวิตระดับโลก
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
}
class AppLifecycleObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
Analytics.shared.onForeground()
NetworkMonitor.shared.start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
Analytics.shared.onBackground()
NetworkMonitor.shared.stop()
}
}
ProcessLifecycleOwner เป็นวิธีที่ถูกต้องเพียงวิธีเดียวในการติดตามการเปลี่ยนไปยังเบื้องหลังในระดับแอปพลิเคชัน ON_START ทำงานเมื่อแอปปรากฏ (เบื้องหน้า) ON_STOP — เมื่อถูกซ่อนอย่างสมบูรณ์ (เบื้องหลัง) แตกต่างจากวงจรชีวิตของ Activity ตรงที่ ProcessLifecycleOwner ไม่ผูกติดกับหน้าจอเฉพาะ สำหรับ Jetpack Compose ให้ใช้ LifecycleResumeEffect หรือ collectAsStateWithLifecycle แทนการสังเกตด้วยตนเอง
ViewModel + Lifecycle เป็นสถาปัตยกรรม Android ที่ถูกต้องสำหรับการจัดการวงจรชีวิต ViewModel จะถูกทำความสะอาดโดยอัตโนมัติเมื่อ onDestroy ป้องกันการรั่วไหล
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class TimerViewModel : ViewModel() {
private val _time = MutableLiveData<Long>()
val time: LiveData<Long> = _time
private var startTime = System.currentTimeMillis()
init {
updateTime()
}
private fun updateTime() {
_time.value = System.currentTimeMillis() - startTime
}
override fun onCleared() {
super.onCleared()
Logger.d("ViewModel cleared — กำลังปลดปล่อยทรัพยากร")
}
}
// ใน Activity
class TimerActivity : AppCompatActivity() {
private val viewModel: TimerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Logger.d("Activity created")
}
override fun onResume() {
super.onResume()
viewModel.time.observe(this) { Logger.d("Time: $it") }
}
override fun onPause() {
super.onPause()
Logger.d("Activity paused — กำลังหยุดแอนิเมชัน")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — ทำความสะอาด")
}
}
ViewModel อยู่รอดการหมุนหน้าจอและจะถูกทำลายเมื่อ Activity สิ้นสุดเท่านั้น onCleared() เป็นสถานที่สำหรับปลดปล่อยทรัพยากรและยกเลิกการสมัครรับข้อมูลสตรีม LiveData หยุดการสังเกตโดยอัตโนมัติเมื่อ onPause และเริ่มใหม่เมื่อ onResume — ซึ่งให้การป้องกันการรั่วไหลและการขัดข้องระหว่างการทำงานเบื้องหลัง ใน Compose ใช้ collectAsStateWithLifecycle() เพื่อจุดประสงค์เดียวกัน
คำถามที่พบบ่อย
เมื่อย่อ แอปจะเปลี่ยนจากทำงานเป็นไม่ทำงาน (ชั่วครู่) จากนั้นไปยังเบื้องหลัง หลังจากนั้นไม่กี่วินาที ระบบอาจย้ายไปยัง suspended — โค้ดหยุดทำงาน หน่วยความจำถูกสงวน เมื่อหน่วยความจำต่ำ ระบบจะยุติแอปที่ถูกระงับ (willTerminate ไม่ถูกเรียก) บันทึกข้อมูลของคุณใน applicationDidEnterBackground — นี่คือจุดที่รับประกันสุดท้ายสำหรับการบันทึก
ขั้นต่ำที่จำเป็น: applicationDidEnterBackground — บันทึกข้อมูลผู้ใช้; applicationWillEnterForeground — อัปเดตส่วนติดต่อผู้ใช้; ใน Android onPause — หยุดแอนิเมชัน/เซ็นเซอร์ชั่วคราว ProcessLifecycleOwner ทำให้การจัดการง่ายขึ้น: onResume — onPause สำหรับเบื้องหน้า, onStart — onStop สำหรับการมองเห็น จัดการสถานะอื่นตามความจำเป็น — ไม่ใช่ทุกสถานะที่สำคัญสำหรับทุกแอปพลิเคชัน
AppDelegate เป็นตัวแทนระดับโลกของทั้งแอปพลิเคชัน (การเริ่มต้น การเปลี่ยนไปยังเบื้องหลัง) SceneDelegate (iOS 13+) จัดการวงจรชีวิตของ หน้าต่างเฉพาะ (ฉาก) — ในแอปพลิเคชัน iPad หลายหน้าต่าง แต่ละฉากมีวงจรชีวิตของตัวเอง สำหรับ iOS 12 และต่ำกว่า วงจรชีวิตทั้งหมดถูกจัดการผ่าน AppDelegate ด้วย iPadOS 16+ จำเป็นต้องใช้ SceneDelegate สำหรับ Stage Manager
ProcessLifecycleOwner เป็นคอมโพเนนต์จาก AndroidX Lifecycle ที่ติดตามวงจรชีวิตของทั้งแอปพลิเคชัน (กระบวนการ) ไม่ใช่ Activity แต่ละตัว มันให้สองเหตุการณ์หลัก: ON_RESUME (แอปในเบื้องหน้า) และ ON_STOP (แอปในเบื้องหลัง) ใช้สำหรับการเริ่มต้น/ทำความสะอาด SDK, การวิเคราะห์, ตัวตรวจสอบเครือข่าย — ทุกสิ่งที่ควรตอบสนองต่อการเปลี่ยนไปยังเบื้องหลังของแอป โดยไม่ขึ้นกับหน้าจอปัจจุบัน
ใน Jetpack Compose ให้ใช้ LifecycleEventObserver ผ่าน LocalLifecycleOwner.current.lifecycle หรือฟังก์ชัน collectAsStateWithLifecycle() สำหรับหน้าจอ Compose ให้ใช้ LifecycleResumeEffect — คล้ายกับ DisposableEffect ที่ทำงานเมื่อ onResume และทำความสะอาดเมื่อ onPause lifecycleScope.launchWhenResumed — โครูทีนที่ถูกระงับโดยอัตโนมัติในเบื้องหลัง
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ