อธิบายเรื่องการใช้ Kotlin Coroutines ใน Android แบบรวบรัด

Boonya Kitpitak
Black Lens
Published in
3 min readAug 31, 2019

ช่วงหลังๆมานี่เพื่อนๆน่าจะเคยได้ยินเรื่องของ 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 ตัว
  1. launch: launch เรียกได้ว่าเป็น Builder ที่ถูกใช้บ่อย เพราะเป็นวิธีที่ง่ายที่สุดในการสร้าง Coroutine โดย Coroutine ที่ถูกสร้างขึ้นจะไม่ไป Block Thread ที่กำลัง ทำงานอยู่
  2. async : async เป็น Builder ที่สามารถ Return ค่าออกมาจาก Coroutine ได้ โดยจะยังไม่ขอพูดเรื่อง builder ตัวนี้ในเชิงลึก ถ้ามีโอกาสจะเล่าให้ฟังในบทความต่อๆไป
  3. 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 นี้เลยนะครับ

  1. แบบไม่ใช้ 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

Follow me on Github!

https://github.com/benBoonya

--

--

Boonya Kitpitak
Black Lens

Android Developer at Oozou. Also Guitarist and Headbanger