onPause: คืออะไร การบันทึกสถานะ Activity ใน Android

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-03-04 เวลาอ่าน: 10 นาที

onPause เป็นเมธอดวงจรชีวิตของ Android ที่ถูกเรียกเมื่อ Activity สูญเสียโฟกัสการป้อนข้อมูล แต่ยังคงมองเห็นได้บางส่วนบนหน้าจอ ระบบเรียก onPause ก่อนที่ Activity ใหม่จะปรากฏในเบื้องหน้า เมื่อเปิดกล่องโต้ตอบ เมื่อกดปุ่มแอปล่าสุด หรือเมื่อมีการโทรเข้า เมธอดนี้เป็นจุดสุดท้ายที่รับประกันสำหรับการบันทึกข้อมูลผู้ใช้ เนื่องจากหลังจาก onStop และ onDestroy ระบบสามารถยุติกระบวนการโดยไม่ต้องเรียกเพิ่มเติม ภายใน onPause นักพัฒนาจะบันทึกแบบร่าง หยุดแอนิเมชัน ปล่อยกล้อง และเขียน สถานะ UI ปัจจุบันไปยัง SharedPreferences สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวงจรชีวิต Activity แบบเต็ม อ่านบทความ Activity Lifecycle

ประเด็นสำคัญ

  • onPause — Activity สูญเสียโฟกัสแต่ยังคงมองเห็นได้; จุดสุดท้ายที่รับประกันสำหรับการบันทึกข้อมูล
  • การบันทึกสถานะ — ใน onPause จะบันทึกข้อมูลสำคัญของผู้ใช้: แบบร่าง ข้อความในฟอร์ม ความคืบหน้า
  • การปล่อยทรัพยากร — กล้อง ไมโครโฟน เครื่องเล่นวิดีโอจะถูกปล่อยใน onPause เพื่อส่งต่อให้แอปอื่น
  • ข้อจำกัดด้านเวลา — onPause ต้องเสร็จสิ้นภายใน 100 ms; การเกินจะทำให้เกิด ANR และทำให้การเปลี่ยนล่าช้า
  • SharedPreferences.apply() — การเขียนแบบอะซิงโครนัสใน onPause; commit() จะบล็อกเธรดและอาจทำให้เกิด ANR
  • onPause vs onStop — onPause เมื่อมองเห็นบางส่วน (กล่องโต้ตอบ), onStop เมื่อซ่อนทั้งหมด (Activity อื่น)
  • onSaveInstanceState — ถูกเรียกหลังจาก onPause เพื่อบันทึกสถานะชั่วคราวใน Bundle

onPause ใน Android คืออะไร

onPause เป็นเมธอดที่สี่ของวงจรชีวิต Activity ซึ่งถูกเรียกเมื่อหน้าจอสูญเสียโฟกัสการป้อนข้อมูล แต่ยังคงมองเห็นได้บางส่วนสำหรับผู้ใช้ เป็นสถานะ «เปลี่ยนผ่าน» ระหว่างการทำงานของแอปอย่างแอคทีฟและการซ่อน ระบบเรียก onPause ในสถานการณ์ต่อไปนี้: การเปิด Activity อื่น (หน้าจอใหม่ครอบคลุมหน้าจอปัจจุบันบางส่วน), การปรากฏของกล่องโต้ตอบ (Dialog, PopupWindow, Snackbar ไม่เรียก onPause แต่ DialogFragment เรียก), การกดปุ่มแอปล่าสุด, การโทรเข้า, การกดปุ่มเปิดปิดเพื่อล็อกหน้าจอ

ภารกิจหลักของ onPause คือการเตรียมแอปสำหรับความเป็นไปได้ที่จะถูกซ่อนหรือถูกทำลาย นี่คือจุดสุดท้ายในวงจรชีวิตที่นักพัฒนาสามารถมั่นใจได้ว่าโค้ดของเขาจะถูกดำเนินการก่อนที่ระบบจะดำเนินการเปลี่ยนไปยังคอมโพเนนต์อื่น หลังจาก onPause ระบบเรียก onStop (หาก Activity ถูกซ่อนทั้งหมด) หลังจากนั้นกระบวนการสามารถถูกยุติได้ทุกเมื่อโดยไม่ต้องแจ้งเพิ่มเติม

ตามเอกสารของ Android Developers (2025) onPause ควรเบาและรวดเร็วที่สุดเท่าที่จะเป็นไปได้ ตราบใดที่ onPause ยังไม่คืนการควบคุม ระบบไม่สามารถเริ่ม Activity ถัดไปได้ — หมายความว่าผู้ใช้จะเห็นความล่าช้าในการเปลี่ยนหน้าจอ Google แนะนำให้เสร็จสิ้น onPause ภายใน น้อยกว่า 100 มิลลิวินาที และการดำเนินการที่ยาวนานทั้งหมด (บันทึกในฐานข้อมูล เขียนลงดิสก์) ควรดำเนินการแบบอะซิงโครนัสผ่าน coroutines หรือ apply()

onPause ใน Activity

ใน Activity เมธอด onPause ถูกเรียกทุกครั้งที่หน้าจอหยุดทำงานแต่สามารถแสดงผลต่อได้บางส่วน ตัวอย่างทั่วไป: ผู้ใช้เปิดแอปแผนที่ แตะแชร์ตำแหน่ง และกล่องโต้ตอบเลือกระบบปรากฏเหนือแผนที่ Activity แผนที่ได้รับ onPause แต่ยังคงมองเห็นได้ใต้กล่องโต้ตอบ เมื่อกล่องโต้ตอบปิด แผนที่ได้รับ onResume โดยไม่ต้องเรียก onStart (หน้าจอไม่ได้ถูกซ่อนทั้งหมด)

kotlin
class NoteEditorActivity : AppCompatActivity() {
    private var binding: ActivityNoteEditorBinding? = null
    private val prefs by lazy {
        getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
    }

    override fun onPause() {
        super.onPause()

        // บันทึกแบบร่างบันทึก — แบบอะซิงโครนัส
        prefs.edit()
            .putString("draft_title", binding?.titleInput?.text.toString())
            .putString("draft_body", binding?.bodyInput?.text.toString())
            .putLong("draft_timestamp", System.currentTimeMillis())
            .apply()

        // หยุดวิดีโอชั่วคราว
        binding?.videoPlayer?.pause()

        // ปล่อยทรัพยากรเฉพาะ
        releaseCamera()
        releaseAudioFocus()
    }

    override fun onResume() {
        super.onResume()
        // กู้คืนแบบร่าง
        binding?.titleInput?.setText(prefs.getString("draft_title", ""))
        binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
        acquireCamera()
        acquireAudioFocus()
    }
}

ตัวอย่าง NoteEditorActivity แสดงการจัดการ onPause ที่ถูกต้อง: บันทึกแบบร่างใน SharedPreferences ผ่าน apply(), หยุดไฟล์วิดีโอชั่วคราว, ปล่อยกล้องและโฟกัสเสียง แต่ละการเรียกเบาและรวดเร็ว โดยไม่บล็อกเธรด UI นานพอที่จะทำให้เกิด ANR สังเกตลำดับ: super.onPause() ถูกเรียกในบรรทัดแรก — ซึ่งรับประกันว่าลอจิกของระบบจะทำงานแม้เกิดข้อยกเว้นในโค้ดผู้ใช้

การบันทึกสถานะใน onPause

onPause เป็นจุดสุดท้ายที่นักพัฒนาสามารถบันทึกข้อมูลผู้ใช้ได้อย่างน่าเชื่อถือก่อนที่แอปจะถูกซ่อนหรือถูกฆ่าโดยระบบ หลังจาก onStop ระบบสามารถยุติกระบวนการหากหน่วยความจำต่ำ โดยไม่ต้องเรียก onDestroy เมธอด onSaveInstanceState() ถูกเรียกหลังจาก onPause แต่ Bundle ของมันไม่ได้มีไว้สำหรับการจัดเก็บระยะยาว — มันมีชีวิตอยู่จนถึง onCreate ถัดไปเท่านั้น

SharedPreferences กับ apply()

SharedPreferences กับ apply() แบบอะซิงโครนัสเป็นวิธีที่เหมาะสมที่สุดในการบันทึกข้อมูลจำนวนเล็กน้อยใน onPause แตกต่างจาก commit() ซึ่งเขียนข้อมูลแบบซิงโครนัสลงดิสก์และส่งคืน boolean, apply() จะบันทึกข้อมูลในหน่วยความจำทันทีและกำหนดเวลาการเขียนแบบอะซิงโครนัสลงดิสก์ ซึ่งใช้เวลาน้อยกว่า 1 มิลลิวินาทีบนเธรด UI เทียบกับ 10–100 มิลลิวินาทีของ commit()

kotlin
override fun onPause() {
    super.onPause()

    // ❌ ไม่ดี: การเขียนแบบซิงโครนัสบล็อกเธรด
    // prefs.edit().putInt("score", score).commit()

    // ✅ ดี: การเขียนแบบอะซิงโครนัส
    prefs.edit().putInt("score", score).apply()

    // สำหรับออบเจกต์ที่ซับซ้อน — การแคชใน ViewModel
    viewModel.saveState()
}

Room และ Coroutines

สำหรับข้อมูลที่มีโครงสร้าง (SQLite ผ่าน Room) ใน onPause ให้ใช้ coroutines กับ lifecycleScope ViewModelScope จะยกเลิก coroutine โดยอัตโนมัติเมื่อ ViewModel ถูกทำลาย ซึ่งป้องกันการเขียนไปยังฐานข้อมูลที่ปิดแล้ว การเขียนผ่าน Room กับ coroutines ใช้เวลา 5–15 มิลลิวินาทีและไม่บล็อกเธรด UI

kotlin
// ใน ViewModel:
fun saveDraft(title: String, body: String) {
    viewModelScope.launch(Dispatchers.IO) {
        noteDao.insert(NoteDraft(title = title, body = body))
    }
}

// ใน Activity.onPause:
viewModel.saveDraft(
    binding?.titleInput?.text.toString(),
    binding?.bodyInput?.text.toString()
)

onPause ใน Fragment

onPause ใน Fragment จะถูกเรียกเมื่อ Fragment หยุดทำงานแต่สามารถมองเห็นได้ต่อไป สิ่งนี้เกิดขึ้นเมื่อ: Fragment ถูกแทนที่ด้วย Fragment อื่นผ่าน FragmentTransaction; Fragment ไม่ใช่หน้าปัจจุบันใน ViewPager; Activity ที่มี Fragment ได้รับ onPause การโต้ตอบระหว่าง onPause ของ Activity และ onPause ของ Fragment เป็นลำดับชั้นอย่างเคร่งครัด: Activity ได้รับ onPause ก่อน จากนั้น Fragments ทั้งหมดของมัน

kotlin
class MapFragment : Fragment() {
    private var mapController: MapController? = null

    override fun onPause() {
        super.onPause()
        mapController?.stopFollowMode()
        binding?.mapContainer?.alpha = 0.7f
    }

    override fun onResume() {
        super.onResume()
        binding?.mapContainer?.alpha = 1.0f
        if (isVisible) {
            mapController?.startFollowMode()
        }
    }
}

รายละเอียดการทำงานกับแผนที่ใน onPause: Google Maps และ Yandex Maps ใช้ทรัพยากร GPU จำนวนมากในโหมดติดตามแบบแอคทีฟ เมื่อสูญเสียโฟกัส ควรปิดแอนิเมชันแผนที่และลดความถี่ในการอัปเดตมาร์กเกอร์ และเมื่อโฟกัสกลับมา ให้คืนค่าฟังก์ชันการทำงานเต็มรูปแบบ สิ่งนี้ช่วยปรับปรุงประสิทธิภาพและลดการใช้พลังงานเมื่อสลับระหว่างหน้าจอ

onPause vs onStop: ความแตกต่างและสถานการณ์

หนึ่งในความสับสนที่พบบ่อยที่สุดในหมู่นักพัฒนา Android มือใหม่คือการไม่เข้าใจความแตกต่างระหว่าง onPause และ onStop มาตรวจสอบแต่ละสถานการณ์และกำหนดเมธอดที่ถูกต้อง

สถานการณ์onPauseonStop
การเปิดหน้าต่างกล่องโต้ตอบถูกเรียกไม่ถูกเรียก
การเปิด Activity ใหม่ (ไม่โปร่งใส)ถูกเรียกถูกเรียก
การกดปุ่มโฮมถูกเรียกถูกเรียก
การล็อกหน้าจอถูกเรียกถูกเรียก
การโทรเข้าถูกเรียกถูกเรียก
Activity โปร่งใสทับด้านบนถูกเรียกไม่ถูกเรียก
Split Screen (ครึ่งหน้าจอ)ถูกเรียกไม่ถูกเรียก
PiP (ภาพซ้อนภาพ)ถูกเรียกไม่ถูกเรียก

กฎหลัก: onPause ถูกเรียกเมื่อสูญเสียโฟกัสใดๆ, onStop ถูกเรียกเมื่อสูญเสียการมองเห็นโดยสมบูรณ์เท่านั้น หาก Activity ยังคงมองเห็นได้ (แม้บางส่วน) onStop จะไม่ถูกเรียก สิ่งนี้สำคัญอย่างยิ่งสำหรับโหมด Split Screen, PiP และ Activity โปร่งใส — ที่นี่ onPause/onResume ทำงาน แต่ onStart/onStop ไม่ทำงาน

จังหวะเวลาและประสิทธิภาพของ onPause

onPause เป็นเมธอดวงจรชีวิตที่สำคัญที่สุดในแง่ของเวลา เพราะมันบล็อกการเรนเดอร์ของ Activity ถัดไป ระบบรอให้ onPause ของ Activity ปัจจุบันเสร็จสิ้นก่อนที่จะแสดง Activity ใหม่ หาก onPause ใช้เวลานานกว่า 100 มิลลิวินาที ผู้ใช้จะสังเกตเห็นความล่าช้าในการเปลี่ยน หากนานกว่า 5 วินาที ระบบจะแสดง ANR

คำแนะนำด้านประสิทธิภาพ

คู่มือประสิทธิภาพ Android ของ Google (2025) ให้คำแนะนำต่อไปนี้สำหรับ onPause: อย่าทำคำขอเครือข่าย — ควรยกเลิกหรือย้ายไปยัง WorkManager; อย่าเขียนไฟล์ขนาดใหญ่ลงดิสก์ — ใช้ BufferedWriter ในเธรดพื้นหลัง; อย่าดำเนินการค้นหา SQL ที่ซับซ้อน — การดำเนินการ Room ควรเป็นแบบอะซิงโครนัสผ่าน coroutines; หลีกเลี่ยงการสร้างออบเจกต์ใหม่ — การเก็บขยะใน onPause ทำให้ความล่าช้าแย่ลง; ใช้ apply() แทน commit() สำหรับ SharedPreferences

kotlin
override fun onPause() {
    super.onPause()

    // ❌ ไม่ดี: คำขอ HTTP บล็อก UI
    // val response = api.syncSave(data).execute()

    // ❌ ไม่ดี: การเขียนไฟล์แบบซิงโครนัส
    // FileOutputStream(file).write(data)

    // ✅ ดี: การบันทึกแบบอะซิงโครนัส
    lifecycleScope.launch {
        withContext(Dispatchers.IO) {
            api.saveData(data)
            fileDao.write(data)
        }
    }

    // ✅ ดี: การเขียนเบาใน SharedPreferences
    prefs.edit().putString("key", value).apply()
}

การสร้างโปรไฟล์ onPause ผ่าน Android Studio Profiler (ร่องรอย CPU) แสดงเวลาดำเนินการที่แน่นอน หาก onPause ใช้เวลามากกว่า 100 ms Profiler จะเน้นเมธอดเป็นสีเหลือง และมากกว่า 500 ms เป็นสีแดง ในโครงการเชิงพาณิชย์ของ IT Sectr เราใช้การทดสอบ Macrobenchmark ที่ตรวจสอบเวลาเปลี่ยนระหว่าง Activity โดยอัตโนมัติและส่งสัญญาณการลดลงของประสิทธิภาพในไปป์ไลน์ CI

ข้อผิดพลาดทั่วไปใน onPause

แม้นักพัฒนาที่มีประสบการณ์ก็ทำผิดพลาดใน onPause มาตรวจสอบปัญหาทั่วไปห้าข้อและวิธีแก้ไข

การเขียนฐานข้อมูลแบบซิงโครนัส

การเรียก Room DAO ด้วยคำค้นหาแบบซิงโครนัส (.executeAsObservable() โดยไม่มี coroutines) ใน onPause บล็อกเธรด UI เป็นเวลา 10–50 ms หากเกิด GC หรือการแข่งขันเขียนในเวลาเดียวกัน ความล่าช้าอาจถึง 200–500 ms วิธีแก้: ใช้ coroutines กับ Dispatchers.IO หรือ apply() สำหรับ SharedPreferences

การลงทะเบียน Listener ใหม่

onPause ไม่ใช่ที่สำหรับลงทะเบียน listener หากคุณลงทะเบียน BroadcastReceiver ใน onPause มันจะยังคงทำงานเมื่อ Activity ไม่มองเห็นอีกต่อไป การลงทะเบียนควรทำเฉพาะใน onStart/onResume และใน onPause/onStop — เฉพาะการยกเลิกการลงทะเบียนเท่านั้น ข้อยกเว้นคือ API ที่ขับเคลื่อนด้วย Intent ซึ่งต้องการการลงทะเบียนก่อนการเรียก

การละเลยข้อยกเว้น

หากเกิดข้อยกเว้นที่ไม่ถูกจัดการใน onPause ระบบจะไม่เรียก onStop และ onDestroy Activity จะค้างในสถานะที่ไม่แน่นอน และ onResume เมื่อกลับมาอาจไม่สามารถกู้คืนทรัพยากรที่ปล่อยไปแล้วได้อย่างถูกต้อง วิธีแก้: ห่อการดำเนินการที่สำคัญใน try/catch ด้วยการบันทึกผ่าน Log.e()

การบันทึกข้อมูลที่ซ้ำซ้อน

ไม่จำเป็นต้องบันทึกใน onPause ข้อมูลที่สามารถกู้คืนได้ง่าย ตัวอย่างเช่น ผลลัพธ์คำขอ API จะถูกแคชใน Room หรือ DataStore ในเวลาที่ได้รับ ไม่ใช่ใน onPause บันทึกเฉพาะสิ่งที่ผู้ใช้ป้อนด้วยตนเองและไม่สามารถกู้คืนได้โดยอัตโนมัติ — ข้อความในฟิลด์ รายการที่เลือก ตำแหน่งเลื่อน

ลืม super.onPause()

ควรเรียก super.onPause() แต่แตกต่างจาก onCreate การละเว้นมันไม่ทำให้เกิดการขัดข้องทันที ระบบ «ให้อภัย» การขาด super ใน onPause แต่เครื่องสถานะภายในเข้าสู่สถานะที่ไม่ถูกต้อง การเรียก onResume ครั้งถัดไปอาจไม่สามารถกู้คืนโฟกัสการป้อนข้อมูล ทำให้ Activity «ค้าง» เรียก super.onPause() โดยเร็วที่สุดเสมอ

คำถามที่พบบ่อย

จะเกิดอะไรขึ้นหากเรียก finish() ใน onPause?

การเรียก finish() ใน onPause จะยุติ Activity ทันทีหลังจากกลับจากเมธอด นี่เป็นสถานการณ์ที่ถูกต้องหากจำเป็นต้องปิดหน้าจอเมื่อสูญเสียโฟกัส (เช่น หน้าจอตรวจสอบสิทธิ์เมื่อย่อแอป) อย่างไรก็ตาม finish() เริ่มวงจรการยุติเต็ม: onStop onDestroy ซึ่งเพิ่มความล่าช้าในการเปลี่ยน ใช้ finish() ใน onPause เฉพาะเมื่อจำเป็นจริงๆ

onPause แตกต่างจาก onSaveInstanceState อย่างไร?

onPause ใช้สำหรับบันทึกข้อมูลที่ควรอยู่รอดหลังการยุติกระบวนการ (แบบร่างใน SharedPreferences/Room) onSaveInstanceState ใช้สำหรับบันทึกสถานะ UI ชั่วคราว ที่จำเป็นจนถึง onCreate ถัดไปเท่านั้น (ตำแหน่งเลื่อน แท็บที่เลือก) Bundle ของ onSaveInstanceState จะไม่ถูกเก็บรักษาเมื่อแอปถูกยุติโดยสมบูรณ์ — มันมีอยู่ในหน่วยความจำเท่านั้น ข้อมูล onPause ถูกบันทึกบนดิสก์และอยู่รอดหลังจากรีบูต

สามารถเปิดกล่องโต้ตอบใน onPause ได้หรือไม่?

ไม่แนะนำ การเปิดกล่องโต้ตอบหรือป๊อปอัปใน onPause ทำให้เกิด WindowLeakException หาก Activity ถูกยุติไปแล้ว หากจำเป็นต้องแสดงการแจ้งเตือนเมื่อสูญเสียโฟกัส ให้ใช้ NotificationManager (การแจ้งเตือนระบบ) — ปลอดภัยและผู้ใช้คาดหวัง สำหรับการดำเนินการที่เลื่อนออกไป ให้ใช้ AlarmManager หรือ WorkManager

ทำไม onPause จึงเป็นจุดบันทึกที่รับประกัน แต่ onStop ไม่ใช่?

onPause รับประกันว่าจะถูกเรียกก่อนที่ Activity จะหยุดทำงาน onStop อาจไม่ถูกเรียกหากระบบฆ่ากระบวนการเพื่อเพิ่มหน่วยความจำ — ในกรณีนี้ onDestroy ก็ไม่ถูกเรียกเช่นกัน onPause เป็นเมธอดเดียวหลังจาก onResume ที่ถูกเรียกเสมอ โดยไม่ขึ้นกับสาเหตุของการสูญเสียโฟกัส ดังนั้น ข้อมูลสำคัญทั้งหมดจึงถูกบันทึกใน onPause

วิธีทดสอบ onPause ในการทดสอบหน่วย

สำหรับการทดสอบ onPause ใช้ Robolectric หรือ FragmentScenario จาก AndroidX Test FragmentScenario.create() moveToState(State.STARTED) moveToState(State.RESUMED) moveToState(State.STARTED) เรียก onPause ตามลำดับ จากนั้นตรวจสอบว่าข้อมูลถูกบันทึกใน SharedPreferences หรือกล้องถูกปล่อยผ่านออบเจกต์จำลอง Robolectric 4.12+ รองรับการจำลอง onPause/onResume โดยไม่ต้องใช้อุปกรณ์จริง

สรุป

  • onPause — Activity สูญเสียโฟกัสการป้อนข้อมูลแต่ยังคงมองเห็นได้บางส่วน; จุดสุดท้ายที่รับประกันสำหรับการบันทึกข้อมูล
  • การบันทึก — SharedPreferences.apply() หรือ Room ผ่าน coroutines; commit() และการดำเนินการแบบซิงโครนัสต้องห้าม
  • การปล่อยทรัพยากร — กล้อง โฟกัสเสียง เครื่องเล่นวิดีโอถูกปล่อยใน onPause เพื่อส่งต่อให้แอปอื่น
  • ขีดจำกัด 100 ms — onPause บล็อกการเรนเดอร์ของ Activity ถัดไป; การเกินขีดจำกัดทำให้เกิด ANR
  • onPause vs onStop — onPause เมื่อสูญเสียโฟกัส (การมองเห็นยังคงอยู่), onStop เมื่อซ่อนทั้งหมด
  • Fragment.onPause — การเรียกตามลำดับชั้นหลังจาก Activity.onPause; รายละเอียดสำหรับแผนที่และ ViewPager
  • ข้อผิดพลาดทั่วไป — การเขียนแบบซิงโครนัส การลงทะเบียน listener การละเลย try/catch การบันทึกซ้ำซ้อน
  • super.onPause() — เรียกให้เร็วที่สุด; การละเว้นไม่ทำให้ขัดข้องแต่ทำให้เครื่องสถานะเสียหาย

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม