อธิบายเรื่องการใช้ Kotlin Coroutines ใน Android แบบรวบรัด
ช่วงหลังๆมานี่เพื่อนๆน่าจะเคยได้ยินเรื่องของ Coroutine กันมาเยอะว่ามันดี อย่างนู้นอย่างนี้ แต่ไม่มีเวลาได้ศึกษามันอย่างจริงๆจังๆซักที ในบทความนี้ผมจะพยายามสรุปเรื่องของ Coroutine และ Concept พื้นฐานอย่างรวบรัด เผื่อเพื่อนๆคนไหนอยากเอาไปเริ่มต้นใช้งานใน Android Application ของตัวเอง
ถ้าจะว่ากันโดยย่อแล้ว Kotlin Coroutine คือการเขียน โค้ดในรูปแบบ Asynchronous ด้วยวิธีที่อ่านง่ายและกระชับขึ้นโดยไม่ต้องใช้ CallBack มากมาย
เพื่อให้ไม่เป็นการเสียเวลาลองดู Code ด้านล่างกันเลยครับ
ใน Code จะมี ViewModel class โดย class นี้ได้ใช้ Coroutine เพื่อ Fetch ข้อมูล Object Artist จาก Api โดยการเรียก getArtistUseCase
ซึ่งถูก Inject เข้ามาใน Constructor
ก่อนจะไปต่ออยากแนะนำให้รู้จักกับสิ่งสำคัญด้านล่างนี้ก่อน
- สิ่งแรกที่ทุกคนควรรู้จักคือ Coroutine Context
Coroutine Context ใช้เพื่อกำหนดว่าจะให้ Coroutine ของเรา Execute แบบไหน เช่น จะ Run บน
UI Thread
หรือIO Thread(ส่วนใหญ่เอาไว้ยิง API)
โดยการกำหนด Coroutine Context นั้นสามารถทำได้ผ่านDispatcher
Dispatchers ที่ ใช้งานบ่อยมากๆจะมีสองแบบ Dispatchers.Main
จะใช้เวลาเราต้องการให้ Coroutine ทำงานเกี่ยวกับ UI Thread
, ส่วน Dispatchers.IO
จะใช้กับ Task ที่จะ Block Thread ปัจจุบัน เช่น ยิง API Request, การเข้าถึง Database หรือ Files
- ต่อมาคือ Coroutine Builder เจ้าตัวนี้ใช้สร้าง Coroutine ใหม่ขึ้นมาโดยหลักๆแล้วจะมี Builder ที่สำคัญอยู่ 3 ตัว
launch
: launch เรียกได้ว่าเป็น Builder ที่ถูกใช้บ่อย เพราะเป็นวิธีที่ง่ายที่สุดในการสร้าง Coroutine โดย Coroutine ที่ถูกสร้างขึ้นจะไม่ไป Block Thread ที่กำลัง ทำงานอยู่async
: async เป็น Builder ที่สามารถ Return ค่าออกมาจาก Coroutine ได้ โดยจะยังไม่ขอพูดเรื่อง builder ตัวนี้ในเชิงลึก ถ้ามีโอกาสจะเล่าให้ฟังในบทความต่อๆไปrunBlocking
: ต่างจากlaunch
runBlocking จะ block Thread ที่กำลังทำงานอยู่ (ฟังแล้วอาจดูแปลกๆเพราะ จุดประสงค์ของ Coroutine คือการทำงานแบบ Asynchoronous ไม่ใช่เหรอ? แต่จริงๆแล้ว runBlocking มีประโยชน์มากเวลาเขียน test ให้กับ Coroutine นั่นเอง)
ทีนี้กลับมาที่ตัวอย่างของเรากัน เราจะมาสร้าง Coroutine เพื่อดึงข้อมูลมาจาก API กัน
การจะสร้าง Coroutine ขึ้นมานั้นเราต้องกำหนด Coroutine Context ให้กับ Coroutine Builder ของเรากันก่อน ในตัวอย่างของเรานี้ เรากำหนด Coroutine Context โดยใช้ CoroutineScope
class ArtistViewModel(
private val getArtistsUseCase: GetArtistsUseCase
): ViewModel(), CoroutineScope { private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main ... override fun onCleared() {
super.onCleared()
job.cancel()
}
}
ในที่นี้เราให้ class ArtistViewModel
implement CoroutineScope
ซึ่งเราก็จะต้อง override coroutineContext
ด้วย ซึ่งนี้เป็นการกำหนด Coroutine Context ให้กับ Coroutine ใน ViewModel ของเรา
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
การประกาศแบบนี้ เป็นการบอกว่า Default Dispatcher ของ Coroutine ใน Class นี้คือ Dispatchers.Main
(ย้ำกันอีกทีว่า Dispatcher.Main คือการกำหนดให้ Coroutine ทำงานบน UI Thread
)และ การที่มี job
นั้นทำให้เราสามารถ เรียกjob.cancel()
ได้ทุกเมื่อเวลาที่เราต้องการยกเลิก การทำงานของ Coroutine ที่อยู่ใน Class นี้
ในที่นี้เราจะเรียก job.cancel()
ใน function onCleared()
เพื่อให้ Coroutine ของเราหยุดทำงานในกรณีที่ viewModel โดนทำลายไป
หลังจากการกำหนด Scope ของ Coroutine กันไปแล้วเราก็มาเริ่มสร้าง Coroutine กันเลย
launch {
val response = withContext(Dispatchers.IO) {
getArtistsUseCase("Slayer")
}
artistsLiveData.value = response?.result
}
ในที่นี้ต้องเราเรียก getArtistsUseCase(“Slayer”)
ซึ่งเป็น UseCase ที่ไปเรียก API เพื่อให้ได้ response ที่เราต้องการกลับมา ซึ่งอย่างที่บอกไปข้างต้นว่าเราต้องใช้ Dispatcher.IO
ถ้าต้องการทำ task ที่เกี่ยวข้องกับการเรียก API ดังนั้นเราจึงต้องเปลี่ยน Coroutine Context จาก Default เราตั้งไว้เป็น Dispatcher.Main
ให้กลายเป็นDispatcher.IO
ก่อนด้วย function withContext
จากนั้นพอเราได้ response กลับมาจาก Server ค่าของมันก็จะถูก Assign ลง artistsLiveData
เท่านี้ก็เรียบร้อย
ในตัวอย่างที่กล่าวไปนี้สามารถนำ Concept ไปประยุกต์ใช้กับอย่างอื่นที่ไม่ใช่ viewModel
ได้เหมือนกัน ไม่ว่าจะเป็นใน Presenter
, Activity
, หรือ Fragment
จริงๆแล้วถ้าเราใช้ Android ViewModel ใน AndroidX lifecycle v2.1.0 ได้มีการเพิ่ม viewModelScope
ทำให้เราไม่ต้องไปเรียก job.cancel()
เองใน onClear()
ด้วยล่ะ โดย default แล้ว Coroutine context ของ viewModelScope
จะถูกกำหนดเป็น Dispatchers.Main
สามารถดูตัวอย่างการใช้งานได้ตามด้านล่างนี้
ส่วนใครที่อยากลอง ดู Code แล้วลองเล่นดูก็สามารถเข้าไปดูได้ที่ Link นี้เลยนะครับ
- แบบไม่ใช้
viewModelScope
2. แบบที่ใช้ viewModelScope
ถ้าชอบก็กด ⭐️ ไว้ที่ repository กันหน่อยนะคร๊าบ
ก่อนจากกันก็ขอฝาก page ด้วย
รบกวนกดติดตาม และ Like Page “โค้ดไม่คิด” ตามลิงค์ด้านล่างได้เลยครับ เพื่อรอรับข่าวสารได้เลยครับ 😀
https://www.facebook.com/codewithoutbrain/
💁♂️ อยากเพิ่ม Productivity ในการทำงานรึเปล่า?
ลองใช้แอป Ergo Pomodoro ที่พัฒนาโดย page ของเรากันเถอะ แอปนี้ใช้การจับเวลาแบบ Pomodoro เพื่อเพิ่มประสิทธิภาพการทำงานให้ดียิ่งขึ้น
✅ Android: bit.ly/3pZOuEE
✅ iOS: https://apps.apple.com/us/app/ergonomic-pomodoro-timer/id1550788893