CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global
ติดตั้ง CoroutineExceptionHandler เพื่อบันทึกหรือกู้คืนจาก exception ที่ไม่ถูกจัดการ
CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
CoroutineExceptionHandler คืออะไร
CoroutineExceptionHandler เป็นองค์ประกอบของบริบทที่จัดการข้อยกเว้นที่ไม่มีการจับจากโครูทีน ซึ่งไม่มีตัวจัดการ catch โดยทำหน้าที่เป็นทางเลือกสุดท้าย
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { context, exception ->
println("Caught unhandled: ${exception.message}")
}
fun main() = runBlocking {
val scope = CoroutineScope(Dispatchers.Default + handler)
scope.launch { throw RuntimeException("oops") }
delay(100)
}ใช้ได้เฉพาะกับโครูทีนระดับราก
CoroutineExceptionHandler จะจับข้อยกเว้นจากโครูทีน ระดับราก เท่านั้น (โครูทีนที่เปิดใช้งานโดยตรงบนขอบเขต) ส่วนโครูทีนลูกจะส่งต่อข้อยกเว้นไปยังโครูทีนแม่ ไม่ใช่ไปยังตัวจัดการ
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
// Root coroutine — handler fires:
CoroutineScope(handler).launch {
throw RuntimeException("root error")
}
delay(100)
// NOT handler (child of coroutineScope):
// launch { launch { throw RuntimeException() } }
}ไม่ได้ระงับข้อยกเว้น
ตัวจัดการจะถูกเรียกใช้ หลังจากข้อยกเว้นได้ยกเลิกโครูทีนไปแล้ว ตัวจัดการนี้ใช้สำหรับการบันทึกเหตุการณ์ การรายงานการหยุดทำงาน หรือการล้างทรัพยากร ไม่ใช่สำหรับทำให้การทำงานดำเนินต่อ
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e ->
println("[CrashReport] ${e::class.simpleName}: ${e.message}")
// send to Crashlytics, Sentry, etc.
}
fun main() = runBlocking {
CoroutineScope(SupervisorJob() + handler).apply {
launch { throw IllegalStateException("state error") }
launch { delay(100); println("still alive") }
delay(200)
cancel()
}
}ตัวจัดการกับ SupervisorJob
การใช้ SupervisorJob + CoroutineExceptionHandler ร่วมกันเป็นรูปแบบมาตรฐานสำหรับขอบเขตที่มีอายุการใช้งานยาวนาน โครูทีนลูกจะล้มเหลวแยกจากกัน และความล้มเหลวที่ไม่มีการจัดการจะถูกบันทึกไว้
import kotlinx.coroutines.*
class AppScope {
private val handler = CoroutineExceptionHandler { _, e ->
println("Uncaught: ${e.message}")
}
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
}
fun main() = runBlocking {
val app = AppScope()
app.scope.launch { throw RuntimeException("task failed") }
app.scope.launch { delay(100); println("other task ok") }
delay(200)
app.scope.cancel()
}ตัวจัดการกับ async
สำหรับ async ข้อยกเว้นจะถูกเก็บไว้ใน Deferred และถูกส่งออกที่ await() ตัวจัดการจะไม่ทำงานสำหรับ async เว้นแต่จะไม่มีการรอ deferred นั้น
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
val scope = CoroutineScope(SupervisorJob() + handler)
val deferred = scope.async { throw RuntimeException("async error") }
try {
deferred.await() // exception thrown here
} catch (e: RuntimeException) {
println("Caught from await: ${e.message}")
}
delay(50)
scope.cancel()
}การเปรียบเทียบกับ Thread.UncaughtExceptionHandler
ต่างจาก UncaughtExceptionHandler ของ Java ตัวจัดการของ Kotlin เป็นส่วนหนึ่งของบริบทโครูทีน และมีผลเฉพาะกับโครูทีนภายในขอบเขตนั้นเท่านั้น
import kotlinx.coroutines.*
// Java style (applies to threads):
Thread.setDefaultUncaughtExceptionHandler { t, e ->
println("Thread ${t.name} threw: ${e.message}")
}
// Kotlin coroutine style (applies to coroutines in scope):
val handler = CoroutineExceptionHandler { _, e ->
println("Coroutine threw: ${e.message}")
}
fun main() = runBlocking { println("Handlers target different concurrency models") }รูปแบบการบันทึกเหตุการณ์ด้วย MDC
ในแอปเซิร์ฟเวอร์ ตัวจัดการสามารถเก็บข้อมูลจากบริบทโครูทีน เช่น รหัสคำขอ เพื่อใช้บันทึกเหตุการณ์แบบมีโครงสร้างก่อนส่งต่อไปยังเฟรมเวิร์กสำหรับบันทึกเหตุการณ์
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { ctx, e ->
val jobName = ctx[CoroutineName]?.name ?: "unknown"
println("[${jobName}] ERROR: ${e.message}")
}
fun main() = runBlocking {
CoroutineScope(SupervisorJob() + handler).launch(CoroutineName("DataLoader")) {
throw RuntimeException("fetch failed")
}
delay(100)
}การผสานรวมการรายงานการหยุดทำงาน
ใช้ตัวจัดการส่งต่อข้อยกเว้นที่ไม่มีการจับไปยังบริการรายงานการหยุดทำงาน เช่น Firebase Crashlytics หรือ Sentry
import kotlinx.coroutines.*
object CrashReporter {
fun record(e: Throwable) = println("[Crashlytics] ${e.message}")
}
val handler = CoroutineExceptionHandler { _, e ->
if (e !is CancellationException) CrashReporter.record(e)
}
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
fun main() = runBlocking {
appScope.launch { throw RuntimeException("unhandled in production") }
delay(100)
appScope.cancel()
}การสืบทอดตัวจัดการ
ตัวจัดการจากบริบทแม่จะไม่ถูกสืบทอดโดยอัตโนมัติไปยังโครูทีนลูก ตัวจัดการต้องอยู่ในบริบทของโครูทีนระดับรากจึงจะทำงาน
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
// Handler only fires at root level:
CoroutineScope(SupervisorJob() + handler).launch {
// Child of root — exception propagates to root handler:
launch { throw RuntimeException("nested") }
}
delay(100)
}สรุปแนวทางปฏิบัติที่ดี
ติดตั้ง CoroutineExceptionHandler บนขอบเขตระดับแอปหรือระดับคุณลักษณะเสมอ บันทึกข้อยกเว้นที่ไม่ใช่การยกเลิกทุกกรณี อย่าพึ่งพาตัวจัดการนี้เพื่อควบคุมลำดับการทำงาน เพราะมีไว้สำหรับการสังเกตการณ์เท่านั้น
import kotlinx.coroutines.*
val globalHandler = CoroutineExceptionHandler { ctx, e ->
if (e !is CancellationException) {
println("[ERROR] ${ctx[CoroutineName]?.name}: ${e.message}")
}
}
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + globalHandler)
fun main() = runBlocking {
appScope.launch(CoroutineName("Auth")) { throw RuntimeException("token expired") }
delay(100); appScope.cancel()
}ตรวจสอบความเข้าใจอย่างรวดเร็ว
CoroutineExceptionHandler ไม่ทำงานโดยอัตโนมัติกับโครูทีนประเภทใด
ทบทวน
CoroutineExceptionHandler เป็นตัวสังเกตการณ์ทางเลือกสุดท้ายสำหรับข้อยกเว้นที่ไม่มีการจับในโครูทีนระดับราก ใช้ร่วมกับ SupervisorJob สำหรับขอบเขตที่มีอายุการใช้งานยาวนาน ใช้สำหรับการบันทึกเหตุการณ์และการรายงานการหยุดทำงานเท่านั้น ไม่ใช่สำหรับควบคุมลำดับการทำงาน
คำถามที่พบบ่อย
บทเรียน “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global”
ติดตั้ง CoroutineExceptionHandler เพื่อบันทึกหรือกู้คืนจาก exception ที่ไม่ถูกจัดการ คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม
ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- SupervisorJob กับ Job: การแยกความล้มเหลว
- CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global
- การส่งต่อ Exception ของ async/await
- การออกแบบสถาปัตยกรรม Coroutine ที่ทนทาน