lifecycleScope เป็น CoroutineScope ในตัวจากไลบรารี androidx.lifecycle ที่เชื่อมโยงกับวงจรชีวิตของ Activity, Fragment หรือ LifecycleOwner ใดๆ และยกเลิก coroutine โดยอัตโนมัติเมื่อคอมโพเนนต์ถูกทำลาย ตาม Google Android Developers, 2025 lifecycleScope ช่วยให้คุณเรียกใช้ coroutine ที่เกี่ยวข้องกับเลเยอร์ UI ได้อย่างปลอดภัย โดยไม่มีความเสี่ยงที่จะเรียกใช้โค้ดหลังจาก Activity หรือ Fragment ถูกทำลาย Scope จะถูกยกเลิกโดยอัตโนมัติเมื่อ LifecycleOwner เปลี่ยนไปเป็นสถานะ DESTROYED
ประเด็นสำคัญ
lifecycleScope เป็นคุณสมบัติส่วนขยายบนอินเทอร์เฟซ LifecycleOwner (Activity, Fragment, Service) ที่ให้ CoroutineScope พร้อมใช้ เชื่อมโยงกับวงจรชีวิตทั้งหมดของคอมโพเนนต์ เมื่อ LifecycleOwner ถึงสถานะ DESTROYED lifecycleScope จะยกเลิก coroutine ที่ทำงานอยู่ทั้งหมดโดยอัตโนมัติ
// In Fragment or Activity
lifecycleScope.launch {
delay(1000)
showSnackbar("สวัสดี!")
}
แตกต่างจาก viewModelScope ตรงที่ lifecycleScope จะถูกยกเลิกทุกครั้งที่ LifecycleOwner ถูกทำลาย — รวมถึงการหมุนหน้าจอ ทำให้เหมาะสำหรับการดำเนินการที่ควรมีชีวิตอยู่เฉพาะเมื่อหน้าจอเฉพาะกำลังแสดงอยู่
lifecycleScope พร้อมใช้งานทุกที่มี LifecycleOwner:
กลไกการยกเลิกอัตโนมัติของ lifecycleScope ขึ้นอยู่กับการสมัครรับเหตุการณ์ Lifecycle เมื่อ Lifecycle ลดลงต่ำกว่า CREATED ไปยัง DESTROYED scope จะถูกยกเลิก
| สถานะ | คำอธิบาย | Scope ทำงาน |
|---|---|---|
| CREATED | LifecycleOwner ถูกสร้างขึ้น, onCreate ถูกเรียกใช้ | ใช่ |
| STARTED | LifecycleOwner มองเห็นได้ (onStart) | ใช่ |
| RESUMED | LifecycleOwner อยู่เบื้องหน้า (onResume) | ใช่ |
| DESTROYED | LifecycleOwner ถูกทำลาย (onDestroy) | ไม่ (scope ถูกยกเลิก) |
lifecycleScope ถูกสร้างขึ้นเป็น CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) และจัดเก็บภายใน Lifecycle เมื่อ Lifecycle เปลี่ยนไปเป็นสถานะ DESTROYED จะเรียก scope.cancel() กลไกนี้ถูกใช้งานผ่าน LifecycleEventObserver ซึ่งสมัครรับเหตุการณ์วงจรชีวิตเมื่อเข้าถึง scope ครั้งแรก
เมื่อหมุนหน้าจอ Activity จะถูกทำลาย (onDestroy) และสร้างใหม่ lifecycleScope จะถูกยกเลิกพร้อมกับ Activity เก่า และอินสแตนซ์ scope ใหม่จะถูกสร้างขึ้นสำหรับ Activity ใหม่ นี่คือความแตกต่างพื้นฐานจาก viewModelScope ซึ่งคงอยู่เมื่อหมุน
ไลบรารี lifecycle มีหลายวิธีในการเริ่ม coroutine ผ่าน lifecycleScope มาดูวิวัฒนาการของ API จากเมธอดที่เลิกใช้แล้วไปจนถึงสมัยใหม่
วิธีที่ง่ายที่สุดคือ lifecycleScope.launch { ... } coroutine เริ่มต้นทันทีและถูกยกเลิกที่ DESTROYED อย่างไรก็ตาม มันสามารถเรียกใช้โค้ดได้แม้เมื่อ UI ไม่ปรากฏ (เช่น ในพื้นหลังหลังจาก onStop) ซึ่งไม่เป็นที่ต้องการเสมอไป
เมธอดเหล่านี้จะหยุดการทำงานของ coroutine ชั่วคราวเมื่อ Lifecycle ลดลงต่ำกว่าสถานะที่ระบุ และดำเนินการต่อเมื่อกลับมา อย่างไรก็ตาม พวกมันถูกทำเครื่องหมายเป็น @Deprecated ใน lifecycle-runtime-ktx 2.6.0 เพราะ:
repeatOnLifecycle เป็นวิธีที่ Google แนะนำในการเริ่ม coroutine ที่ซิงค์กับวงจรชีวิต มันยกเลิกและเริ่ม coroutine ใหม่ทุกครั้งที่ Lifecycle ถึงสถานะที่ระบุ
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
coroutine ที่ส่งไปยัง repeatOnLifecycle จะเริ่มเมื่อ Lifecycle ถึง STARTED และจะถูกยกเลิกเมื่อลดลงต่ำกว่า STARTED เมื่อกลับสู่ STARTED coroutine จะเริ่มต้นใหม่ตั้งแต่ต้น ปลอดภัยและมีประสิทธิภาพ — ไม่มี coroutine ใดถูกระงับค้างอยู่
สำหรับการรวบรวมข้อมูลจาก Flow โดยรับรู้วงจรชีวิต มีโอเปอเรเตอร์ flowWithLifecycle ซึ่งจะหยุดและเริ่มการรวบรวมโดยอัตโนมัติเมื่อสถานะ Lifecycle เปลี่ยนแปลง:
viewModel.uiState
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { state -> updateUI(state) }
.launchIn(lifecycleScope)
โอเปอเรเตอร์ flowWithLifecycle เป็นวิธีที่สั้นที่สุดในการสมัครรับ Flow อย่างปลอดภัยในเลเยอร์ UI
มาดูสามสถานการณ์จริงของการใช้ lifecycleScope ในแอปพลิเคชัน Android ด้วย Kotlin
class MapFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.observeLocation().collect { loc ->
updateMapMarker(loc)
}
}
}
}
}
coroutine เริ่มเมื่อ fragment ปรากฏ (STARTED) และถูกยกเลิกเมื่อออกจากหน้าจอ (STOPPED) หากผู้ใช้เปลี่ยนไปใช้แอปอื่น การอัปเดตตำแหน่งจะไม่กินแบตเตอรี่
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
แอนิเมชันจะทำงานเมื่อ fragment อยู่ในเบื้องหน้าเท่านั้น (RESUMED) หากผู้ใช้ย่อแอประหว่างแอนิเมชัน coroutine จะถูกยกเลิก และเมื่อกลับมา แอนิเมชันจะเริ่มใหม่
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
ข้อมูลจะถูกซิงค์ทุก 30 วินาที แต่เฉพาะเมื่อหน้าจอมองเห็น isActive ตรวจสอบว่า coroutine ถูกยกเลิกหรือไม่ ซึ่งเป็นวิธีที่ปลอดภัยในการออกจากลูปเมื่อออกจากหน้าจอ
ทั้งสอง scope เชื่อมโยงกับวงจรชีวิต แต่กับคนละด้าน การเข้าใจความแตกต่างเป็นสิ่งสำคัญสำหรับสถาปัตยกรรมแอปพลิเคชัน Android ที่ถูกต้อง
viewModelScope เชื่อมโยงกับ ViewModel ซึ่งคงอยู่เมื่อหมุนหน้าจอ lifecycleScope เชื่อมโยงกับ LifecycleOwner (Activity/Fragment) ซึ่งถูกทำลายและสร้างใหม่เมื่อหมุน สิ่งนี้กำหนดสถานการณ์การใช้งาน
ในทางปฏิบัติ การรวมกันของทั้งสอง scope เป็นเรื่องปกติ: viewModelScope โหลดข้อมูลและจัดการสถานะ ในขณะที่ lifecycleScope สมัครรับ Flow จาก ViewModel โดยรับรู้วงจรชีวิต การแยกความรับผิดชอบนี้ถือเป็น แนวปฏิบัติที่ดีที่สุด ในการพัฒนา Android สมัยใหม่
มาดูสี่ข้อผิดพลาดทั่วไปที่นักพัฒนาทำเมื่อใช้ lifecycleScope
หากคุณเริ่มการโหลดข้อมูลใน lifecycleScope.launch coroutine จะถูกยกเลิกเมื่อหมุนหน้าจอและข้อมูลจะต้องโหลดใหม่ ใช้ viewModelScope สำหรับการดำเนินการที่อายุยืน lifecycleScope มีไว้สำหรับงานที่เชื่อมโยงกับ UI เท่านั้น
การเรียก viewModel.someFlow.collect { ... } โดยตรงภายใน lifecycleScope.launch จะยังคงรวบรวมข้อมูลต่อไปแม้เมื่อหน้าจอไม่ปรากฏ สิ่งนี้อาจทำให้เกิดการอัปเดต UI ในพื้นหลังและค่าใช้จ่ายที่ไม่จำเป็น ใช้ repeatOnLifecycle หรือ flowWithLifecycle เสมอ
แม้ว่า lifecycleScope จะถูกยกเลิกที่ DESTROYED แต่โค้ดหลังจากจุดระงับ (suspend) อาจไม่ทำงานเมื่อถูกยกเลิกกะทันหัน อย่าพึ่งพาการทำงานของโค้ดหลังจากการเรียก suspend เว้นแต่คุณจะใช้ NonCancellable
launchWhenStarted และสิ่งที่คล้ายกันไม่ยกเลิก coroutine แค่หยุดชั่วคราว หากหน้าจอสลับระหว่างเบื้องหน้าและพื้นหลังหลายครั้ง coroutine จะสะสมการเรียกที่ถูกเลื่อน เปลี่ยนไปใช้ repeatOnLifecycle — นี่เป็นวิธีที่ถูกต้องเท่านั้นในการซิงค์กับ Lifecycle
คำถามที่พบบ่อย
lifecycleScope จะถูกยกเลิกโดยอัตโนมัติเมื่อ LifecycleOwner ถูกทำลาย GlobalScope มีชีวิตอยู่ตลอดเวลาที่แอปพลิเคชันทำงาน coroutine ใน lifecycleScope ไม่สามารถอัปเดต UI หลังจากคอมโพเนนต์ถูกทำลาย ในขณะที่ GlobalScope สามารถทำได้ ซึ่งทำให้เกิดข้อขัดข้อง ใช้ lifecycleScope ในเลเยอร์ UI เสมอ
ไม่ ViewModel ไม่ใช่ LifecycleOwner ดังนั้น lifecycleScope จึงไม่พร้อมใช้งานในนั้น ViewModel ใช้ viewModelScope หากโค้ดต้องทำงานในทั้งสองบริบท ให้แยกตรรกะออกไปยัง use case หรือ repository ด้วยฟังก์ชัน suspend
การเรียก repeatOnLifecycle แต่ละครั้งจะสร้าง coroutine ใหม่ที่เรียกใช้บล็อกเมื่อถึงสถานะ Lifecycle ที่ระบุ หากเรียก repeatOnLifecycle สองครั้งสำหรับสถานะเดียวกัน ทั้งสองบล็อกจะทำงานอย่างอิสระ โดยทั่วไปการเรียกครั้งเดียวใน onViewCreated ก็เพียงพอ
คุณไม่สามารถเปลี่ยนตัวจัดส่งของ lifecycleScope ได้โดยตรง — มันใช้ Dispatchers.Main.immediate ภายในบล็อก coroutine คุณสามารถสลับไปยังตัวจัดส่งอื่นผ่าน withContext สำหรับการทดสอบ ให้ใช้ TestDispatcher กับ LifecycleOwner
lifecycleScope จะถูกยกเลิกเมื่อ LifecycleOwner เปลี่ยนไปเป็นสถานะ DESTROYED (หลัง onDestroy) การเรียก lifecycleScope.launch อย่างง่ายจะไม่ถูกยกเลิกใน onPause หรือ onStop หากต้องการหยุดชั่วคราวเมื่อไปที่พื้นหลัง ให้ใช้ repeatOnLifecycle(STARTED) หรือ repeatOnLifecycle(RESUMED)
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม