# Kotlin Coroutines สำหรับ Android: คู่มือฉบับสมบูรณ์ 2026 > คู่มือครบถ้วนเกี่ยวกับ Kotlin coroutines ในการพัฒนา Android: suspend functions, scopes, dispatchers และ patterns ขั้นสูง - Published: 2026-01-04 - Updated: 2026-04-02 - Author: Anthony Fillion-Maillet - Tags: kotlin, coroutines, android, async, concurrency - Reading time: 12 min --- Kotlin coroutines ได้เปลี่ยนแปลงการเขียนโปรแกรมแบบอะซิงโครนัสบน Android อย่างสิ้นเชิง ยุคของ callback hell และ AsyncTask ที่ล้าสมัยได้ผ่านพ้นไปแล้ว ด้วย coroutines โค้ดอะซิงโครนัสสามารถเขียนให้อ่านเหมือนโค้ดซิงโครนัส พร้อมทั้งรักษาประสิทธิภาพและความสามารถในการบำรุงรักษาได้อย่างดี > **ทำไมต้อง coroutines?** > > Coroutines มีน้ำหนักเบา (สามารถรันได้หลายพัน coroutine บน thread เดียว) รองรับการยกเลิกโดยอัตโนมัติ และทำงานร่วมกับ Jetpack และระบบนิเวศ Android สมัยใหม่ได้อย่างราบรื่น ## ทำความเข้าใจพื้นฐานสำคัญ ก่อนเริ่มเขียนโค้ด สิ่งสำคัญคือการทำความเข้าใจว่าอะไรทำให้ coroutines มีประสิทธิภาพสูง ### Coroutine คืออะไร? Coroutine คือหน่วยการคำนวณที่สามารถหยุดชั่วคราวได้ ต่างจาก thread ตรงที่ coroutine ไม่ได้บล็อก (block) การทำงาน แต่จะหยุดชั่วคราว (suspend) แล้วปล่อย thread ให้ทำงานอื่นได้ ```kotlin // BasicCoroutine.kt import kotlinx.coroutines.* fun main() = runBlocking { launch { delay(1000L) // Suspend without blocking println("World!") } println("Hello,") } // Output: Hello, World! ``` ### Suspend Functions: หัวใจของ Coroutines คีย์เวิร์ด `suspend` บ่งบอกว่าฟังก์ชันนั้นสามารถหยุดการทำงานของ coroutine ชั่วคราวได้โดยไม่บล็อก thread ```kotlin // SuspendFunction.kt suspend fun fetchUserData(userId: String): User { return withContext(Dispatchers.IO) { // Network call - runs on an IO thread apiService.getUser(userId) } } suspend fun fetchUserWithPosts(userId: String): UserWithPosts { // Sequential execution val user = fetchUserData(userId) val posts = fetchUserPosts(userId) return UserWithPosts(user, posts) } ``` > **กฎสำคัญ**: suspend function สามารถเรียกใช้ได้จาก suspend function อื่นหรือจากภายใน coroutine เท่านั้น ## Coroutine Scopes: การจัดการวงจรชีวิต Scope กำหนดวงจรชีวิตของ coroutines ซึ่งเป็นปัจจัยสำคัญในการป้องกัน memory leak ### viewModelScope: Scope สำหรับ ViewModel ```kotlin // UserViewModel.kt class UserViewModel( private val userRepository: UserRepository ) : ViewModel() { private val _uiState = MutableStateFlow(UserUiState.Loading) val uiState: StateFlow = _uiState.asStateFlow() fun loadUser(userId: String) { viewModelScope.launch { _uiState.value = UserUiState.Loading try { val user = userRepository.getUser(userId) _uiState.value = UserUiState.Success(user) } catch (e: Exception) { _uiState.value = UserUiState.Error(e.message) } } } } sealed class UserUiState { object Loading : UserUiState() data class Success(val user: User) : UserUiState() data class Error(val message: String?) : UserUiState() } ``` ### lifecycleScope: สำหรับ Activity และ Fragment ```kotlin // UserFragment.kt class UserFragment : Fragment() { private val viewModel: UserViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { state -> when (state) { is UserUiState.Loading -> showLoading() is UserUiState.Success -> showUser(state.user) is UserUiState.Error -> showError(state.message) } } } } } } ``` > **ระวัง memory leak** > > ไม่ควรใช้ `GlobalScope` ในแอปพลิเคชัน Android เด็ดขาด Coroutines ที่เปิดด้วย `GlobalScope` ไม่ได้ผูกกับวงจรชีวิตใด จึงอาจทำให้เกิด memory leak ได้ ## Dispatchers: การควบคุม Thread การทำงาน Dispatchers กำหนดว่า coroutine จะทำงานบน thread ใด ### Dispatcher หลัก 4 ตัว ```kotlin // Dispatchers.kt // Main: main thread (UI) viewModelScope.launch(Dispatchers.Main) { textView.text = "UI update" } // IO: I/O operations (network, database) viewModelScope.launch(Dispatchers.IO) { val data = repository.fetchFromNetwork() } // Default: CPU-intensive computations viewModelScope.launch(Dispatchers.Default) { val result = heavyComputation(data) } // Unconfined: inherits caller context (rare use) ``` ### withContext: การสลับ Dispatcher ```kotlin // ImageProcessor.kt class ImageProcessor { suspend fun processImage(bitmap: Bitmap): Bitmap { return withContext(Dispatchers.Default) { // CPU-intensive processing on Default applyFilters(bitmap) } } suspend fun saveToGallery(bitmap: Bitmap) { withContext(Dispatchers.IO) { // Disk write on IO saveToFile(bitmap) } } } // Usage in ViewModel viewModelScope.launch { val processed = imageProcessor.processImage(originalBitmap) imageProcessor.saveToGallery(processed) // Automatic return to Main for UI update _uiState.value = UiState.Success(processed) } ``` ## การทำงานแบบขนานด้วย async/await เมื่อต้องการทำงานหลายอย่างพร้อมกันและรวมผลลัพธ์เข้าด้วยกัน ให้ใช้ `async` ```kotlin // ParallelExecution.kt suspend fun loadDashboard(): Dashboard { return coroutineScope { // Parallel launch val userDeferred = async { userRepository.getUser() } val statsDeferred = async { statsRepository.getStats() } val notificationsDeferred = async { notificationRepository.getNotifications() } // Await results Dashboard( user = userDeferred.await(), stats = statsDeferred.await(), notifications = notificationsDeferred.await() ) } } ``` > **ประสิทธิภาพ** > > เมื่อใช้ `async` ทั้ง 3 การเรียกจะทำงานแบบขนาน หากแต่ละการเรียกใช้เวลา 1 วินาที เวลารวมจะอยู่ที่ประมาณ 1 วินาทีแทนที่จะเป็น 3 วินาทีเมื่อทำงานแบบลำดับ ## การจัดการข้อผิดพลาดใน Coroutines ### try/catch แบบดั้งเดิม ```kotlin // ErrorHandling.kt viewModelScope.launch { try { val user = userRepository.getUser(userId) _uiState.value = UiState.Success(user) } catch (e: HttpException) { _uiState.value = UiState.Error("Server error: ${e.code()}") } catch (e: IOException) { _uiState.value = UiState.Error("Network error") } catch (e: Exception) { _uiState.value = UiState.Error("Unexpected error") } } ``` ### CoroutineExceptionHandler ```kotlin // ExceptionHandler.kt class UserViewModel : ViewModel() { private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> _uiState.value = UiState.Error(throwable.message) Timber.e(throwable, "Error in coroutine") } fun loadUser(userId: String) { viewModelScope.launch(exceptionHandler) { val user = userRepository.getUser(userId) _uiState.value = UiState.Success(user) } } } ``` ### Result Wrapper Pattern ```kotlin // ResultPattern.kt sealed class Result { data class Success(val data: T) : Result() data class Error(val exception: Throwable) : Result() } suspend fun safeApiCall(apiCall: suspend () -> T): Result { return try { Result.Success(apiCall()) } catch (e: Exception) { Result.Error(e) } } // Usage class UserRepository(private val api: UserApi) { suspend fun getUser(id: String): Result = safeApiCall { api.getUser(id) } } // In ViewModel viewModelScope.launch { when (val result = userRepository.getUser(userId)) { is Result.Success -> _uiState.value = UiState.Success(result.data) is Result.Error -> _uiState.value = UiState.Error(result.exception.message) } } ``` ## การยกเลิก Coroutine: การจัดการทรัพยากรอย่างถูกต้อง Coroutines รองรับการยกเลิกแบบร่วมมือ (cooperative cancellation) ซึ่งเป็นสิ่งจำเป็นในการป้องกันการรั่วไหลของทรัพยากร ### การยกเลิกอัตโนมัติด้วย Scopes ```kotlin // SearchViewModel.kt class SearchViewModel : ViewModel() { private var searchJob: Job? = null fun search(query: String) { // Cancel previous search searchJob?.cancel() searchJob = viewModelScope.launch { delay(300) // Debounce val results = searchRepository.search(query) _searchResults.value = results } } } ``` ### การตรวจสอบสถานะการยกเลิก ```kotlin // CancellationCheck.kt suspend fun processLargeList(items: List) { items.forEach { item -> // Check if coroutine is cancelled ensureActive() processItem(item) } } suspend fun downloadFiles(urls: List) = coroutineScope { urls.map { url -> async { try { downloadFile(url) } catch (e: CancellationException) { cleanupPartialDownload(url) throw e // Re-throw to propagate cancellation } } }.awaitAll() } ``` > **ห้ามกลืน CancellationException เด็ดขาด**: หากมีการ catch `Exception` ต้อง throw `CancellationException` ออกไปใหม่เสมอ เพื่อให้การยกเลิกแพร่กระจายได้อย่างถูกต้อง ## Flow: การเขียนโปรแกรมแบบรีแอคทีฟ Flow เปรียบเสมือน RxJava Observable ในโลกของ coroutines แต่ใช้งานง่ายกว่าและถูกรวมเข้ากับระบบตั้งแต่แรก ### การสร้างและเก็บข้อมูลจาก Flow ```kotlin // FlowBasics.kt fun getUsers(): Flow> = flow { while (true) { val users = userApi.getUsers() emit(users) delay(5000) // Poll every 5 seconds } } // Flow from Room @Dao interface UserDao { @Query("SELECT * FROM users") fun getAllUsers(): Flow> } // Collecting in ViewModel viewModelScope.launch { userDao.getAllUsers() .catch { e -> _uiState.value = UiState.Error(e.message) } .collect { users -> _uiState.value = UiState.Success(users) } } ``` ### StateFlow กับ SharedFlow ```kotlin // StateFlowVsSharedFlow.kt class EventViewModel : ViewModel() { // StateFlow: keeps last value, ideal for UI state private val _uiState = MutableStateFlow(UiState.Initial) val uiState: StateFlow = _uiState.asStateFlow() // SharedFlow: for one-shot events (navigation, snackbar) private val _events = MutableSharedFlow() val events: SharedFlow = _events.asSharedFlow() fun onButtonClick() { viewModelScope.launch { _events.emit(UiEvent.NavigateToDetail) } } } sealed class UiEvent { object NavigateToDetail : UiEvent() data class ShowSnackbar(val message: String) : UiEvent() } ``` ### ตัวดำเนินการ Flow ที่สำคัญ ```kotlin // FlowOperators.kt userRepository.getUsers() .map { users -> users.filter { it.isActive } } .distinctUntilChanged() .debounce(300) .flatMapLatest { users -> fetchUserDetails(users) } .catch { e -> emit(emptyList()) } .onEach { users -> analytics.logUserCount(users.size) } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() ) ``` ## Patterns ขั้นสูง ### Retry ด้วย Exponential Backoff ```kotlin // RetryPattern.kt suspend fun retryWithBackoff( times: Int = 3, initialDelay: Long = 100, maxDelay: Long = 1000, factor: Double = 2.0, block: suspend () -> T ): T { var currentDelay = initialDelay repeat(times - 1) { attempt -> try { return block() } catch (e: Exception) { Timber.w("Attempt ${attempt + 1} failed, retrying in ${currentDelay}ms") } delay(currentDelay) currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay) } return block() } // Usage val user = retryWithBackoff { userApi.getUser(userId) } ``` ### Timeout ```kotlin // TimeoutPattern.kt suspend fun fetchWithTimeout() { try { val result = withTimeout(5000L) { api.fetchData() } processResult(result) } catch (e: TimeoutCancellationException) { showError("Request took too long") } } // Or with a default value val result = withTimeoutOrNull(5000L) { api.fetchData() } ?: defaultValue ``` ## บทสรุป Kotlin coroutines กลายเป็นเครื่องมือที่ขาดไม่ได้ในการพัฒนา Android สมัยใหม่ Coroutines มอบแนวทางการเขียนโปรแกรมอะซิงโครนัสที่สวยงามและมีประสิทธิภาพ พร้อมทั้งทำงานร่วมกับระบบนิเวศ Jetpack ได้อย่างสมบูรณ์ ### รายการตรวจสอบ - ใช้ `viewModelScope` และ `lifecycleScope` เพื่อป้องกัน memory leak - เลือก Dispatcher ที่เหมาะสม (Main, IO, Default) - จัดการข้อผิดพลาดด้วย try/catch หรือ Result wrapper - นำระบบการยกเลิกแบบร่วมมือไปใช้งาน - ใช้ Flow สำหรับกระแสข้อมูลแบบรีแอคทีฟ - เลือก StateFlow สำหรับสถานะ UI และ SharedFlow สำหรับเหตุการณ์ การเชี่ยวชาญ coroutines จะมอบข้อได้เปรียบที่สำคัญทั้งในโปรเจกต์ Android และการสัมภาษณ์เชิงเทคนิค การฝึกฝนอย่างสม่ำเสมอและการสำรวจ patterns ขั้นสูงจะช่วยยกระดับทักษะการพัฒนาได้อย่างมาก --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/android/mastering-kotlin-coroutines