onStop — เมธอดของวงจรชีวิต Activity ใน Android ซึ่งถูกเรียกโดยระบบเมื่อ Activity ไม่ปรากฏแก่ผู้ใช้อีกต่อไป Activity จะเปลี่ยนไปยังสถานะ Stopped หลังจากที่ Activity ใหม่ครอบคลุมมันอย่างสมบูรณ์ หรือเมื่อแอปพลิเคชันถูกย่อ ในเมธอด onStop นักพัฒนาต้องหยุดแอนิเมชัน ปล่อยทรัพยากรกล้องและเซนเซอร์ และบันทึกร่างของข้อมูลที่ป้อน ตาม Android Vitals (Google, 2025) การจัดการ onStop อย่างถูกต้องจะลดจำนวน ANR (แอปพลิเคชันไม่ตอบสนอง) เมื่อย่อแอปพลิเคชันลง 35% หลังจาก onStop ระบบสามารถเรียก onRestart (กลับไปยังหน้าจอ) หรือ onDestroy (สิ้นสุดสมบูรณ์) เอกสาร Android Developers เกี่ยวกับวงจรชีวิต Activity อธิบาย onStop ว่าเป็นขอบเขตระหว่างสถานะที่มองเห็นและมองไม่เห็น
ประเด็นสำคัญ
onStop — เมธอด callback ของคลาส AppCompatActivity (และคลาส Activity ดั้งเดิม) ซึ่งถูกเรียกโดยระบบปฏิบัติการ Android เมื่อ Activity หยุดปรากฏอย่างสมบูรณ์แก่ผู้ใช้ ในขณะนี้ Activity ถูกซ่อนโดย Activity อื่น หน้าต่างโต้ตอบ ตัวเรียกใช้งานระบบ หรือหน้าจอล็อก จากมุมมองของวงจรชีวิต onStop ตามหลัง onPause และส่งสัญญาณว่า Activity ไม่ปรากฏบนหน้าจออีกต่อไป แม้ว่าวัตถุ Activity และสถานะของมันจะยังคงอยู่ในหน่วยความจำ
เมื่อ Activity เปลี่ยนไปยังสถานะ Stopped (หยุดแล้ว) มันจะคงสถานะไว้ใน RAM — ฟิลด์ทั้งหมด ลำดับชั้น View และ ViewModel ยังคงเข้าถึงได้ ซึ่งแยกความแตกต่างของ Stopped จากสถานะ Destroyed (ถูกทำลาย) ซึ่ง Activity จะถูกลบออกอย่างสมบูรณ์ UI ของระบบสามารถฆ่ากระบวนการของแอปพลิเคชันในสถานะ Stopped เมื่อหน่วยความจำไม่เพียงพอ — นี้เรียกว่า กระบวนการตาย นักพัฒนาต้องบันทึกข้อมูลสำคัญ (ร่าง ตำแหน่งเลื่อน) ใน onSaveInstanceState() ซึ่งถูกเรียกก่อน onStop เพื่อรับประกันการกู้คืนเมื่อกระบวนการตาย
ตามเอกสารคำจำกัดความความเข้ากันได้ของ Android (CDD) สำหรับเวอร์ชัน 14+ กระบวนการในสถานะ Stopped มีลำดับความสำคัญต่ำกว่าสำหรับการถูกฆ่าโดย OOM Killer — ต่ำกว่ากระบวนการในเฟสพื้นหลัง แต่สูงกว่ากระบวนการในแคช ตามสถิติของ Google 68% ของกรณีกระบวนการตายเกิดขึ้นเมื่อ Activity อยู่ในสถานะ Stopped ไม่ใช่ Paused
onStop ถูกเรียกเมื่อ Activity สูญเสียการมองเห็นโดยสมบูรณ์ ไม่ว่าจะด้วยสาเหตุใดก็ตาม: เริ่ม Activity ใหม่ทับ Activity ปัจจุบัน ย่อแอปพลิเคชัน (กด Home) ล็อกหน้าจอ สายเรียกเข้า หรือเปิดโต้ตอบระบบ ในทุกกรณีเหล่านี้ Activity จะได้รับ onPause (สูญเสียโฟกัสบางส่วน) ก่อน แล้วจึง onStop (สูญเสียการมองเห็นโดยสมบูรณ์)
สถานการณ์หลักของการเรียก onStop:
สิ่งสำคัญที่ต้องเข้าใจคือ onStop จะไม่ถูกเรียก เมื่อหมุนหน้าจอ — ในกรณีนี้ Activity จะถูกทำลาย (onPause → onStop → onDestroy) และสร้างใหม่ (onCreate → onStart → onResume) ข้อยกเว้นคือแฟล็ก android:configChanges="orientation" ใน Manifest ซึ่งป้องกันการสร้าง Activity ใหม่และเรียก onConfigurationChanged() แทน
onStop อยู่ในตำแหน่งศูนย์กลางในลำดับวงจรชีวิต Activity ระหว่างสถานะที่มองเห็นและมองไม่เห็น ลำดับสมบูรณ์: onCreate → onStart → onResume → (สถานะทำงาน) → onPause → onStop → onDestroy (หรือ onRestart → onStart → onResume เมื่อกลับมา)
| สถานะ | เมธอด | การมองเห็น | การโต้ตอบ | หน่วยความจำ |
|---|---|---|---|---|
| Created | onCreate | ไม่ | ไม่ | จัดสรร |
| Started | onStart | บางส่วน | ไม่ | เต็ม |
| Resumed | onResume | เต็ม | ใช่ | เต็ม |
| Paused | onPause | บางส่วน | ไม่ | เต็ม |
| Stopped | onStop | ไม่ | ไม่ | เต็ม* |
| Destroyed | onDestroy | ไม่ | ไม่ | ถูกปล่อย |
*ในสถานะ Stopped Activity จะถูกเก็บไว้ในหน่วยความจำแต่อาจถูกฆ่าโดยระบบเมื่อทรัพยากรไม่เพียงพอ ลำดับความสำคัญของการฆ่ากระบวนการ Stopped คือ รองสุดท้าย เหนือกว่ากระบวนการว่างในแคชเท่านั้น
onStop และ onSaveInstanceState: ระบบเรียก onSaveInstanceState(Bundle) ก่อน onStop เพื่อบันทึกสถานะ UI แบบไดนามิก นักพัฒนาสามารถ Override เมธอดนี้เพื่อบันทึกค่าฟิลด์อินพุต ตำแหน่ง RecyclerView และรายการที่เลือกใน Bundle แม้ว่า Activity จะไม่ถูกทำลาย (ผู้ใช้เพียงย่อและกลับมา) Bundle จะถูกส่งไปยัง onCreate เมื่อมีการเปลี่ยนแปลงการกำหนดค่า Google แนะนำให้บันทึกเฉพาะสถานะ UI ชั่วคราว — ไม่ใช่ข้อมูล Repository หรือ ViewModel ซึ่งอยู่ภายนอก Activity
ใน onStop นักพัฒนาต้องปล่อยทรัพยากรทั้งหมดที่ไม่จำเป็นเมื่อ Activity ไม่ปรากฏ ซึ่งช่วยลดภาระแบตเตอรี่ CPU และหน่วยความจำ และยังป้องกัน ANR เมื่อกลับไปยัง Activity
สิ่งที่ต้องปล่อยใน onStop:
สิ่งที่ไม่ควรทำใน onStop: อย่าดำเนินการที่ใช้เวลานาน — บันทึกข้อมูลจำนวนมากในฐานข้อมูล คำขอเครือข่าย การคำนวณที่ซับซ้อน onStop ทำงานบนเธรดหลักและขัดขวางการกลับไปยัง Activity สำหรับการดำเนินการที่ใช้เวลานาน ให้ใช้ WorkManager กับความล่าช้าหรือ Coroutine ใน viewModelScope อย่าปล่อยทรัพยากร ViewModel — ViewModel อยู่รอดหลังจาก onStop และจะถูกใช้เมื่อกลับมา
onPause และ onStop แตกต่างกันในระดับของการสูญเสียการมองเห็น และขอบเขตของการดำเนินการที่จำเป็น onPause ถูกเรียกเมื่อสูญเสียโฟกัสบางส่วน (เช่น การเปิดหน้าต่างโต้ตอบหรือเมนูระบบ) onStop — เมื่อสูญเสียการมองเห็นโดยสมบูรณ์ ความแตกต่างนี้สำคัญสำหรับการเลือกทรัพยากรที่จะปล่อยในแต่ละขั้นตอน
| คุณลักษณะ | onPause | onStop |
|---|---|---|
| ระดับการมองเห็น | มองเห็นได้บางส่วน | มองไม่เห็นโดยสมบูรณ์ |
| โฟกัส | สูญเสีย | สูญเสีย |
| เวลาดำเนินการ | สูงสุด 500 มิลลิวินาที | สูงสุด 5 วินาที (หมดเวลา ANR) |
| ทรัพยากรที่ต้องปล่อย | สำคัญ (สื่อ, กล้อง) | ทั้งหมดที่มองไม่เห็น (เซนเซอร์, แอนิเมชัน, ตำแหน่ง) |
| การกู้คืน | onResume | onRestart → onStart → onResume |
| ลำดับความสำคัญของกระบวนการ | สูง (เบื้องหน้า) | ปานกลาง (เบื้องหลัง) |
กฎทั่วไป: ใน onPause ให้ปล่อยทรัพยากรระบบที่ ส่งผลทันที ต่อประสบการณ์ผู้ใช้ของแอปพลิเคชันอื่น (กล้อง, เครื่องเล่นสื่อ); ใน onStop — ทรัพยากรอื่นทั้งหมดที่ไม่จำเป็นเมื่อ Activity ถูกซ่อน Google แนะนำให้บันทึกข้อมูลผู้ใช้ที่สำคัญ (ร่างอีเมล, การตั้งค่า) ใน onPause เนื่องจาก onStop อาจไม่ถูกเรียกระหว่างการสลับอย่างรวดเร็ว
เมื่อผู้ใช้กลับไปยัง Activity ที่ถูกซ่อน ระบบจะเรียก onRestart → onStart → onResume เมธอด onRestart ส่งสัญญาณว่า Activity กำลังกลับมาจากสถานะ Stopped นี่เป็นขั้นตอนสำคัญสำหรับการกู้คืน UI และทรัพยากรที่ถูกปล่อยใน onStop
ลำดับการเรียกเมื่อกลับมา:
หากกระบวนการของแอปพลิเคชันถูกฆ่าโดยระบบในสถานะ Stopped onCreate จะถูกเรียกแทน onRestart และ Bundle จาก onSaveInstanceState จะถูกส่งสำหรับการกู้คืนสถานะ สถานการณ์นี้ (กระบวนการตาย) เป็นหนึ่งในสาเหตุที่พบบ่อยที่สุดของบั๊กในแอปพลิเคชัน Android: นักพัฒนาดำเนินการ onRestart แต่ลืมพิจารณาการกู้คืนผ่าน onCreate หลังจากกระบวนการตาย
แสดงการยกเลิกการสมัครเซนเซอร์ที่ถูกต้องและการหยุดแอนิเมชันเมื่อซ่อน Activity เมื่อกลับไปยังหน้าจอ ทรัพยากรจะถูกกู้คืนใน onStart
class MainActivity : AppCompatActivity() {
private lateinit var sensorManager: SensorManager
private var accelerometer: Sensor? = null
private var rotationAnimator: ObjectAnimator? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
override fun onStart() {
super.onStart()
accelerometer?.let {
sensorManager.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_NORMAL)
}
rotationAnimator = ObjectAnimator.ofFloat(findViewById(R.id.icon), "rotation", 0f, 360f)
rotationAnimator?.apply {
duration = 3000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
start()
}
}
override fun onStop() {
super.onStop()
sensorManager.unregisterListener(sensorListener)
rotationAnimator?.cancel()
}
override fun onRestart() {
super.onRestart()
Log.d("MainActivity", "Activity กำลังกลับมาจากสถานะ Stopped")
}
private val sensorListener = SensorEventListener { event, _ ->
Log.d("MainActivity", "ความเร่ง: x=${event.values[0]}, y=${event.values[1]}, z=${event.values[2]}")
}
}
โค้ดลงทะเบียนเซนเซอร์มาตรความเร่งและเริ่มแอนิเมชันหมุนไม่สิ้นสุดใน onStart ใน onStop เซนเซอร์ถูกยกเลิกการลงทะเบียนและแอนิเมชันถูกยกเลิก — ซึ่งป้องกันการใช้แบตเตอรี่เมื่อ Activity ถูกซ่อน หลังจากกลับผ่าน onRestart → onStart ทรัพยากรจะถูกสร้างใหม่
วิธีที่ทันสมัยโดยใช้ ViewModel + SavedStateHandle ข้อมูลฟอร์มจะถูกบันทึกโดยอัตโนมัติระหว่าง onStop โดยไม่ต้องจัดการ Bundle ด้วยตนเอง
class FormViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
var email: String
get() = savedStateHandle["email"] ?: ""
set(value) { savedStateHandle["email"] = value }
var message: String
get() = savedStateHandle["message"] ?: ""
set(value) { savedStateHandle["message"] = value }
}
class FormActivity : AppCompatActivity() {
private val viewModel: FormViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_form)
Log.d("FormActivity", "onCreate: email=${viewModel.email}")
}
override fun onStop() {
super.onStop()
Log.d("FormActivity", "onStop: ข้อมูลถูกบันทึกใน SavedStateHandle")
}
}
SavedStateHandle จะบันทึกค่าใน Bundle โดยอัตโนมัติระหว่าง onSaveInstanceState ซึ่งถูกเรียกก่อน onStop เมื่อหมุนหน้าจอหรือกระบวนการตาย ข้อมูลจะถูกกู้คืนโดยไม่สูญเสีย Google แนะนำ SavedStateHandle สำหรับฟอร์มและร่างแทนการใช้ onSaveInstanceState โดยตรง
การใช้ lifecycleScope กับ Coroutine สำหรับการบันทึกข้อมูลแบบอะซิงโครนัสระหว่างการเปลี่ยนไปยัง onStop Coroutine ทำงานบนตัวจัดส่ง IO โดยไม่บล็อกเธรดหลัก
class NoteActivity : AppCompatActivity() {
private val noteRepository = NoteRepository()
override fun onStop() {
lifecycleScope.launch(Dispatchers.IO) {
val text = findViewById<EditText>(R.id.note_content).text.toString()
noteRepository.saveDraft(text)
withContext(Dispatchers.Main) {
Log.d("NoteActivity", "ร่างถูกบันทึกใน onStop")
}
}
super.onStop()
}
}
Coroutine lifecycleScope.launch จะถูกยกเลิกโดยอัตโนมัติหากวงจรชีวิตของ Activity สิ้นสุดลง การใช้ Dispatchers.IO ช่วยให้แน่ใจว่าการเขียนฐานข้อมูลหรือไฟล์ไม่ขัดขวางการกลับไปยัง Activity ตามข้อมูลของ Google Coroutine ใน lifecycleScope เป็นวิธีที่ต้องการสำหรับการดำเนินการแบบอะซิงโครนัสใน onStop
คำถามที่พบบ่อย
onStop — Activity หยุดปรากฏแต่ยังคงอยู่ในหน่วยความจำในสถานะ Stopped ระบบสามารถนำ Activity กลับผ่าน onRestart onDestroy — Activity ถูกทำลาย หน่วยความจำถูกปล่อย หลังจาก onDestroy การกลับมาทำได้โดยการสร้างอินสแตนซ์ Activity ใหม่ (onCreate) เท่านั้น
ใช่ จำเป็น super.onStop() ช่วยให้มั่นใจว่าส่วนประกอบของระบบทำงานถูกต้อง: Fragment, LoaderManager, ViewModelStore การข้าม super.onStop() อาจทำให้หน่วยความจำรั่วและการกู้คืน Fragment ไม่ถูกต้อง ให้เรียก super.onStop() เสมอเป็นลำดับสุดท้ายหรือลำดับแรก — ลำดับไม่สำคัญ แต่การเรียกเป็นสิ่งจำเป็น
ใช้ Log.d หรือ Timber ในแต่ละเมธอดของวงจรชีวิต เปิดใช้งานตัวกรอง logcat ตามแท็ก Activity ของคุณ สำหรับการผลิต ใช้ Android Vitals — Google รวบรวมเมตริกวงจรชีวิตโดยอัตโนมัติและแสดงความผิดปกติใน Play Console การตรวจสอบวงจรชีวิตยังสามารถใช้ผ่าน ProcessLifecycleOwner
ข้อยกเว้นที่ไม่ถูกจับใน onStop ทำให้เกิด Force Close ของแอปพลิเคชัน ระบบไม่จับข้อยกเว้นใน Callback วงจรชีวิต หากใน onStop มีการดำเนินการที่อาจโยนข้อยกเว้น (การดำเนินการไฟล์, เครือข่าย) ให้ครอบคลุมใน try-catch และบันทึกข้อผิดพลาดโดยไม่ขัดจังหวะการทำงานของ super.onStop()
ไม่ Bitmap ใน Activity จะถูกเก็บโดย GC หากไม่มีข้อมูลอ้างอิงถึงมัน การปล่อยแบบบังคับ (recycle()) ใน onStop ไม่จำเป็นและอาจเป็นอันตราย — หาก Activity กลับผ่าน onRestart จะต้องโหลด Bitmap ใหม่ ใช้ Glide หรือ Coil สำหรับการโหลดรูปภาพ — ไลบรารีเหล่านี้จัดการแคชและวงจรชีวิตโดยอัตโนมัติ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ