onDestroy — เมธอดสุดท้ายของวงจรชีวิตของ Activity และ Fragment ใน Android ที่ถูกเรียกก่อนการทำลายคอมโพเนนต์โดยสมบูรณ์ onDestroy ส่งสัญญาณว่า Activity หรือ Fragment กำลังสิ้นสุดการทำงาน: ทรัพยากรทั้งหมดต้องถูกปล่อย, fragment ที่ซ้อนกันต้องถูกทำลาย, ViewModel ต้องถูกล้าง ตามข้อมูลของ Google onDestroy ถูกเรียกใน 100% ของกรณีการสิ้นสุด Activity แต่ในระหว่างการตายของโพรเซส (process death) ระบบอาจข้ามการเรียก onDestroy โดยสิ้นเชิง เอกสาร Android เกี่ยวกับ onDestroy เน้นว่าเมธอดนี้ไม่รับประกันการเรียกเมื่อการสิ้นสุดผิดปกติ
ประเด็นสำคัญ
onDestroy — เมธอด callback ที่ Android เรียกก่อนที่จะทำลาย Activity หรือ Fragment โดยสมบูรณ์ นี่เป็นโอกาสสุดท้ายสำหรับนักพัฒนาในการปล่อยทรัพยากร ยกเลิกการดำเนินการเบื้องหลัง และสิ้นสุดการทำงานกับข้อมูล หลังจากที่ onDestroy ทำงาน อินสแตนซ์ Activity/Fragment จะถูกทำเครื่องหมายสำหรับการเก็บขยะ (GC) และไม่สามารถใช้งานได้อีกต่อไป
สาเหตุของการเรียก onDestroy:
ตามสถิติ Google Android Vitals (2025) ประมาณ 12% ของกรณีการทำลาย Activity ทั้งหมดเกิดจากการหมุนหน้าจอ 65% เกิดจาก finish() และ 23% เกิดจากการเปลี่ยนแปลงการกำหนดค่า เปอร์เซ็นต์ของการตายของโพรเซสที่ข้าม onDestroy อยู่ที่ประมาณ 5–8% ขึ้นอยู่กับอุปกรณ์ที่มี RAM ต่ำ (น้อยกว่า 4 GB)
onDestroy ถูกเรียกในสถานการณ์มาตรฐานส่วนใหญ่ แต่มีข้อยกเว้นสำคัญที่นักพัฒนาต้องพิจารณา การทำความเข้าใจการรับประกันการเรียก onDestroy มีความสำคัญต่อสถาปัตยกรรมของแอปพลิเคชัน โดยเฉพาะอย่างยิ่งสำหรับการบันทึกข้อมูลและการยกเลิกงาน WorkManager
เมื่อใดที่ onDestroy ถูกเรียก:
เมื่อใดที่ onDestroy ไม่ถูกเรียก:
เนื่องจากการขาดการรับประกันการเรียก onDestroy Google แนะนำ: อย่าพึ่งพา onDestroy ในการบันทึกข้อมูลสำคัญ ใช้ onSaveInstanceState(), WorkManager หรือ Room ที่บันทึกอัตโนมัติ onDestroy มีไว้สำหรับปล่อยทรัพยากร ไม่ใช่เพื่อความคงทน (persistence)
onDestroy มีทั้งสำหรับ Activity และ Fragment แต่มีสัญญาที่แตกต่างกัน วงจรชีวิตของ Fragment มีรายละเอียดมากกว่า: นอกเหนือจาก onDestroy แล้ว ยังมี onDestroyView (การทำลายลำดับชั้น View) และ onDetach (การแยกจาก Activity)
| คอมโพเนนต์ | เมธอดการทำลาย | ลำดับ | ViewModel อยู่รอด |
|---|---|---|---|
| Activity | onDestroy | onPause → onStop → onDestroy | ไม่ (เฉพาะถ้า ViewModelStore ไม่ถูกบันทึก) |
| Fragment | onDestroyView, onDestroy, onDetach | onPause → onStop → onDestroyView → onDestroy → onDetach | ใช่ ถ้า Fragment ไม่ถูกลบ |
ความแตกต่างหลัก: View ของ Fragment ถูกสร้างขึ้นใหม่บ่อยกว่า Fragment เอง ในระหว่างการหมุนหน้าจอ Fragment ผ่าน onDestroyView (การทำลาย View) แต่ Fragment เองและ ViewModel ของมันยังคงมีชีวิต onDestroyView เป็นสถานที่ที่เหมาะสมในการล้างการอ้างอิง View เพื่อป้องกันการรั่วไหลของหน่วยความจำ onDestroy ของ Fragment คล้ายกับ onDestroy ของ Activity ซึ่งถูกเรียกเมื่อ Fragment ถูกลบโดยสมบูรณ์
fragment ลูกถูกทำลายก่อน onDestroy ของ Fragment แม่ ใน Activity fragment ลูกได้รับ onDestroy เมื่อ onDestroy ของ Activity แม่ถูกเรียก ลำดับรับประกัน: fragment สิ้นสุดก่อน Activity ที่บรรจุพวกมัน
onDestroy มีไว้สำหรับปล่อยทรัพยากรทั้งหมดที่ไม่ควรมีอายุยืนกว่า Activity หรือ Fragment ต่างจาก onStop ที่ปล่อยทรัพยากรจนกว่าจะกลับมา onDestroy ทำการล้างครั้งสุดท้าย
รายการตรวจสอบการดำเนินการบังคับใน onDestroy:
สิ่งที่ไม่ควรทำใน onDestroy: อย่าบันทึกข้อมูลใน onDestroy — ใช้ onPause หรือ onSaveInstanceState อย่าเริ่ม Service ใหม่หรืองาน WorkManager — Activity จะถูกทำลายและคุณจะไม่สามารถติดตามผลลัพธ์ได้ อย่าพยายามอัปเดต UI — ลำดับชั้น View ถูกทำลายแล้วหรือกำลังอยู่ในกระบวนการทำลาย การเรียก findViewById() จะคืนค่า null
ViewModel ถูกออกแบบให้อยู่รอดจาก onDestroy ของ Activity ระหว่างการหมุนหน้าจอ แต่ถูกทำลายพร้อมกับ Activity ระหว่าง finish() พฤติกรรมที่ไม่สมมาตรนี้เป็นสาเหตุหลักของความสับสนในหมู่นักพัฒนา
ระหว่างการหมุนหน้าจอ:
ระหว่าง finish() (ผู้ใช้กด “กลับ”):
ดังนั้น การยกเลิก viewModelScope ใน onDestroy ไม่จำเป็น — ViewModel จะทำเอง ถ้าคุณใช้ lifecycleScope (ผูกกับ Activity ไม่ใช่ ViewModel) ให้ยกเลิกมันใน onDestroy ผ่าน lifecycleScope.cancel() หรือจัดการ Job ด้วยตนเอง
แสดงการจัดการ lifecycleScope ที่ถูกต้องใน Activity: coroutine ถูกเริ่มเพื่อติดตามสถานะเครือข่ายและถูกยกเลิกใน onDestroy
class NetworkMonitorActivity : AppCompatActivity() {
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Log.d("NetworkMonitor", "เครือข่ายพร้อมใช้งาน")
}
override fun onLost(network: Network) {
Log.d("NetworkMonitor", "เครือข่ายหายไป")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_network)
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.registerDefaultNetworkCallback(networkCallback)
lifecycleScope.launch {
Log.d("NetworkMonitor", "การตรวจสอบเครือข่ายเริ่มต้นแล้ว")
}
}
override fun onDestroy() {
super.onDestroy()
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.unregisterNetworkCallback(networkCallback)
Log.d("NetworkMonitor", "onDestroy: callback ถูกยกเลิก")
}
}
ใน onDestroy การลงทะเบียน callback เครือข่ายถูกยกเลิก lifecycleScope ถูกยกเลิกโดยอัตโนมัติเมื่อวงจรชีวิตถูกทำลาย — ไม่จำเป็นต้องยกเลิก coroutine แยกต่างหาก callback เครือข่ายต้องถูกยกเลิกการลงทะเบียน มิฉะนั้นมันจะยังคงอยู่ในระบบแม้หลังจาก Activity ถูกทำลาย
Fragment ล้างการอ้างอิง View อย่างถูกต้องใน onDestroyView ป้องกันการรั่วไหลของหน่วยความจำเนื่องจากการปิด (closures)
class ProfileFragment : Fragment() {
private var avatarView: ImageView? = null
private var progressBar: ProgressBar? = null
private val imageLoader = ImageLoader()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
avatarView = view.findViewById(R.id.avatar)
progressBar = view.findViewById(R.id.progress)
loadProfile()
}
private fun loadProfile() {
viewLifecycleOwner.lifecycleScope.launch {
try {
progressBar?.visibility = View.VISIBLE
val bitmap = imageLoader.load("https://example.com/avatar.png")
avatarView?.setImageBitmap(bitmap)
} finally {
progressBar?.visibility = View.GONE
}
}
}
override fun onDestroyView() {
super.onDestroyView()
avatarView = null
progressBar = null
imageLoader.cancel()
}
override fun onDestroy() {
super.onDestroy()
Log.d("ProfileFragment", "onDestroy: Fragment ถูกทำลายโดยสมบูรณ์")
}
}
ใน onDestroyView การอ้างอิง View ถูกตั้งเป็น null — สิ่งนี้ป้องกันการรั่วไหลของหน่วยความจำถ้าการปิดใน imageLoader เก็บการอ้างอิงถึง avatarView Fragment เองและ ViewModel ของมันยังคงมีชีวิตจนถึง onDestroy imageLoader.cancel() ยกเลิกการโหลดถ้า Fragment ออกจากหน้าจอ
การใช้ isFinishing() ช่วยให้แยกแยะว่า Activity สิ้นสุดด้วยคำสั่งผู้ใช้หรือเพื่อการสร้างใหม่
class AnalyticsActivity : AppCompatActivity() {
private val analytics = Analytics()
override fun onDestroy() {
if (isFinishing) {
Log.d("AnalyticsActivity", "Activity สิ้นสุดด้วย finish() — กำลังส่งการวิเคราะห์")
analytics.sendSessionEnd()
} else {
Log.d("AnalyticsActivity", "Activity กำลังถูกสร้างใหม่ (หมุน/กำหนดค่า) — ไม่ส่งการวิเคราะห์")
}
super.onDestroy()
}
}
การตรวจสอบ isFinishing() เป็นรูปแบบที่สำคัญสำหรับการวิเคราะห์ การบันทึก และการล้างข้อมูลเซสชัน ระหว่างการหมุน ไม่ควรส่งเหตุการณ์สิ้นสุดเซสชัน — ผู้ใช้ยังคงทำงานกับแอปพลิเคชัน ตาม Google Analytics การตรวจสอบ isFinishing() ที่ไม่ถูกต้องเป็นสาเหตุของ 40% ของเหตุการณ์เซสชันเท็จ
คำถามที่พบบ่อย
ได้ สามารถเกิดขึ้นได้ — ระหว่างการตายของโพรเซสโดยระบบ การบังคับหยุดโดยผู้ใช้ หรือการสิ้นสุดผิดปกติ ตามข้อมูลของ Google ประมาณ 5–8% ของการสิ้นสุด Activity เกิดขึ้นโดยไม่มีการเรียก onDestroy นักพัฒนาไม่ควรพึ่งพา onDestroy ในการบันทึกข้อมูลสำคัญ — ใช้ onPause หรือ onSaveInstanceState
finish() — การเรียกที่เริ่มต้นการทำลาย Activity onDestroy — callback ที่ถูกเรียกระหว่างการดำเนินการของ finish() finish() จำเป็นสำหรับการเรียก onDestroy ระหว่างการสิ้นสุดปกติ finish() สามารถถูกเรียกโดยระบบหรือนักพัฒนา onDestroy เป็นเพียง callback ของระบบเท่านั้น
ใช่ จำเป็นอย่างยิ่ง ทั้งใน Activity และ Fragment super.onDestroy() รับประกันการล้างที่ถูกต้องของ ChildFragmentManager, LoaderManager และคอมโพเนนต์ระบบอื่นๆ การข้าม super.onDestroy() นำไปสู่การรั่วไหลของหน่วยความจำและข้อบกพร่องในการกู้คืน fragment
onCleared() ถูกเรียกหลังจาก onDestroy ของ Activity หรือ Fragment เมื่อ ViewModel ไม่ต้องการอีกต่อไป ระหว่างการหมุนหน้าจอ onCleared() ไม่ถูกเรียก — ViewModel อยู่รอดจาก onDestroy ลำดับ: onDestroy ของ Activity/Fragment → (ViewModelStore ถูกล้าง) → onCleared()
ในทางเทคนิคได้ แต่ไม่แนะนำ Activity ถูกทำลายทันทีหลังจาก onDestroy และ Service ที่เริ่มแล้วจะไร้การควบคุม สำหรับงานเบื้องหลัง ให้ใช้ WorkManager กับความล่าช้า: WorkManager รับประกันการดำเนินการแม้หลังจาก Activity สิ้นสุดและอยู่รอดจากการตายของโพรเซส
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ