Kotlin นำเสนอโครงสร้างที่ทรงพลังและเบาสำหรับการเขียนโปรแกรมแบบอะซิงโครนัสที่เรียกว่า Coroutines แทนที่จะใช้ callback แบบดั้งเดิม RxJava หรือ Thread คุณสามารถเขียนโค้ดของคุณให้ดูเหมือนซิงโครนัส แต่ทำงานแบบอะซิงโครนัสในเบื้องหลัง ด้วยวิธีนี้ คุณสามารถจัดการการเรียกเครือข่าย การดำเนินการฐานข้อมูล งานที่ใช้เวลานาน และการดำเนินการที่บล็อกได้อย่างง่ายดาย ในบทความนี้ เราจะตรวจสอบแนวคิดพื้นฐานของ coroutines รวมถึง launch, async/await, scope และตัวอย่างการใช้งานทั่วไปอย่างละเอียด
พื้นฐานของ Coroutinesเพื่อใช้ coroutines ก่อนอื่นคุณต้องเพิ่ม dependency (Gradle):
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"โครงสร้างพื้นฐาน:- Coroutine Scope: จัดการวงจรชีวิตของ coroutines (GlobalScope, lifecycleScope, viewModelScope เป็นต้น)
- Coroutine Builder: launch (fire-and-forget), async (คืนค่า)
- Dispatcher: ที่ที่ทำงาน (Main, IO, Default)
ตัวอย่างแรก: launchimport kotlinx.coroutines.*
fun main() {
GlobalScope.launch(Dispatchers.Main) {
println("Coroutine เริ่มต้น - Thread: ${Thread.currentThread().name}")
delay(1000) // การรอแบบไม่บล็อก (ไม่ใช่ sleep!)
println("1 วินาทีต่อมา")
}
println("เธรดหลักดำเนินต่อ")
Thread.sleep(2000) // บล็อกเธรดหลัก (สำหรับทดสอบ)
}delay() ไม่บล็อกเธรด แต่เพียงแค่ระงับ coroutine
async และ awaitสำหรับการดำเนินการอะซิงโครนัสที่คืนค่า:
GlobalScope.launch(Dispatchers.Main) {
val sonuc1 = async(Dispatchers.IO) { ağÇağrısı1() }
val sonuc2 = async(Dispatchers.IO) { ağÇağrısı2() }
println("รวม: ${sonuc1.await() + sonuc2.await()}")
}
fun ağÇağrısı1(): Int {
Thread.sleep(1000)
return 42
}
fun ağÇağrısı2(): Int {
Thread.sleep(1000)
return 58
}การเรียกทั้งสองทำงานแบบขนาน เวลารวมประมาณ 1 วินาที
การใช้งาน Scope และ Context- GlobalScope: ทั่วทั้งแอปพลิเคชัน ใช้อย่างระมัดระวัง (เสี่ยง memory leak)
- lifecycleScope (Android): ผูกกับวงจรชีวิตของ Activity/Fragment
- viewModelScope (ViewModel): ยกเลิกอัตโนมัติกับ ViewModel
ตัวอย่าง Android:class MyViewModel : ViewModel() {
fun veriYukle() {
viewModelScope.launch {
try {
val veri = withContext(Dispatchers.IO) {
repository.veriGetir() // อัปเดต UI
}
} catch (e: Exception) {
// จัดการข้อผิดพลาด
}
}
}
}Structured Concurrency และ Exception HandlingviewModelScope.launch {
try {
val job = launch {
throw Exception("ข้อผิดพลาด!")
}
job.join()
} catch (e: Exception) {
println("จับได้: ${e.message}")
}
}
// ด้วย SupervisorJob ข้อผิดพลาดใน coroutine ลูกไม่กระทบ parent
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)Dispatcher ทั่วไปDispatcher.Main: การอัปเดต UI (Android)
Dispatcher. IO: เครือข่าย ไฟล์ ฐานข้อมูล
Dispatcher.Default: การดำเนินการที่ใช้ CPU สูง (เช่น sorting เป็นต้น)
Dispatcher.Unconfined: สถานการณ์พิเศษ (สำหรับทดสอบ)
แนวปฏิบัติที่ดีที่สุด- ใช้ structured concurrency เสมอ (แทน GlobalScope ใช้ builder ที่มี scope)
- ใช้ Dispatchers.Main ในเธรด UI
- ใช้ withContext() เพื่อเปลี่ยนเธรด
- เพิ่ม try-catch และ CoroutineExceptionHandler สำหรับการจัดการข้อผิดพลาด
- จัดการการไหลข้อมูลแบบ reactive ด้วย Flow (ระดับสูง)
สรุปKotlin coroutines ทำให้การเขียนโปรแกรมแบบอะซิงโครนัสอ่านง่ายและปลอดภัยมากขึ้น
หลุดพ้นจาก callback hell คุณสามารถเขียนโค้ดของคุณให้ดูเหมือนซิงโครนัส เริ่มต้นด้วย launch และ async อย่างง่าย ในโปรเจกต์ Android ดำเนินต่อด้วย lifecycleScope และ viewModelScope
เพื่อฝึกฝน ลองเขียนแอปพลิเคชัน Android ง่ายๆ ที่เรียกเครือข่าย – เช่น แอปพยากรณ์อากาศที่ดึงข้อมูล JSON!