Kotlin Coroutines cho Android: Huong dan day du 2026
Huong dan toan dien ve Kotlin coroutines trong phat trien Android: suspend functions, scopes, dispatchers va cac pattern nang cao.

Kotlin coroutines da thay doi hoan toan cach lap trinh bat dong bo tren Android. Thoi dai cua callback hell va AsyncTask loi thoi da qua: voi coroutines, ma bat dong bo duoc viet giong nhu ma dong bo, dong thoi van dam bao hieu suat va kha nang bao tri.
Coroutines co trong luong nhe (hang ngan coroutine co the chay tren mot thread duy nhat), ho tro huy tu dong, va tich hop lien mach voi Jetpack cung he sinh thai Android hien dai.
Hieu ro cac khai niem co ban
Truoc khi bat dau viet code, can nam vung nhung diem lam cho coroutines tro nen manh me.
Coroutine la gi?
Coroutine la mot the hien cua phep tinh co kha nang tam dung. Khac voi thread, coroutine khong chan (block): chung tam dung thuc thi va giai phong thread cho cac tac vu khac.
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L) // Suspend without blocking
println("World!")
}
println("Hello,")
}
// Output: Hello, World!Suspend Functions: Trai tim cua Coroutines
Tu khoa suspend chi ra rang mot ham co the tam dung viec thuc thi coroutine ma khong chan thread.
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)
}Quy tac vang: Mot suspend function chi co the duoc goi tu mot suspend function khac hoac tu ben trong mot coroutine.
Coroutine Scopes: Quan ly vong doi
Scope xac dinh vong doi cua coroutines. Day la yeu to then chot de tranh ro ri bo nho.
viewModelScope: Scope danh cho ViewModel
class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
val uiState: StateFlow<UserUiState> = _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: Danh cho Activity va Fragment
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)
}
}
}
}
}
}Khong bao gio su dung GlobalScope trong ung dung Android. Cac coroutine khoi chay bang GlobalScope khong gan voi bat ky vong doi nao va co the gay ro ri bo nho.
Dispatchers: Kiem soat luong thuc thi
Dispatchers xac dinh coroutine se chay tren thread nao.
4 Dispatcher chinh
// 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: Chuyen doi Dispatcher
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)
}Thuc thi song song voi async/await
De thuc thi nhieu tac vu dong thoi va ket hop ket qua, su dung async.
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()
)
}
}Voi async, ca 3 loi goi duoc thuc thi song song. Neu moi loi goi mat 1 giay, tong thoi gian chi khoang 1 giay thay vi 3 giay khi thuc thi tuan tu.
Xu ly loi trong Coroutines
try/catch truyen thong
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
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
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
suspend fun <T> safeApiCall(apiCall: suspend () -> T): Result<T> {
return try {
Result.Success(apiCall())
} catch (e: Exception) {
Result.Error(e)
}
}
// Usage
class UserRepository(private val api: UserApi) {
suspend fun getUser(id: String): Result<User> = 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)
}
}Sẵn sàng chinh phục phỏng vấn Android?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Huy coroutine: Don dep tai nguyen dung cach
Coroutines ho tro co che huy hop tac (cooperative cancellation). Day la dieu can thiet de tranh ro ri tai nguyen.
Tu dong huy voi Scopes
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
}
}
}Kiem tra trang thai huy
suspend fun processLargeList(items: List<Item>) {
items.forEach { item ->
// Check if coroutine is cancelled
ensureActive()
processItem(item)
}
}
suspend fun downloadFiles(urls: List<String>) = coroutineScope {
urls.map { url ->
async {
try {
downloadFile(url)
} catch (e: CancellationException) {
cleanupPartialDownload(url)
throw e // Re-throw to propagate cancellation
}
}
}.awaitAll()
}Khong bao gio nuot CancellationException: neu bat Exception, can nem lai CancellationException de viec huy duoc lan truyen dung cach.
Flow: Lap trinh phan ung
Flow la tuong duong cua RxJava Observable trong the gioi coroutines, nhung don gian hon va tich hop san.
Tao va thu thap Flow
fun getUsers(): Flow<List<User>> = 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<List<User>>
}
// Collecting in ViewModel
viewModelScope.launch {
userDao.getAllUsers()
.catch { e -> _uiState.value = UiState.Error(e.message) }
.collect { users ->
_uiState.value = UiState.Success(users)
}
}StateFlow va SharedFlow
class EventViewModel : ViewModel() {
// StateFlow: keeps last value, ideal for UI state
private val _uiState = MutableStateFlow(UiState.Initial)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
// SharedFlow: for one-shot events (navigation, snackbar)
private val _events = MutableSharedFlow<UiEvent>()
val events: SharedFlow<UiEvent> = _events.asSharedFlow()
fun onButtonClick() {
viewModelScope.launch {
_events.emit(UiEvent.NavigateToDetail)
}
}
}
sealed class UiEvent {
object NavigateToDetail : UiEvent()
data class ShowSnackbar(val message: String) : UiEvent()
}Cac toan tu Flow thiet yeu
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()
)Cac pattern nang cao
Retry voi Exponential Backoff
suspend fun <T> 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
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()
} ?: defaultValueKet luan
Kotlin coroutines da tro thanh cong cu khong the thieu trong phat trien Android hien dai. Chung mang den cach tiep can bat dong bo thanh lich va hieu qua, tich hop hoan hao voi he sinh thai Jetpack.
Danh sach kiem tra
- Su dung
viewModelScopevalifecycleScopede tranh ro ri bo nho - Chon dung Dispatcher (Main, IO, Default)
- Xu ly loi bang try/catch hoac Result wrapper
- Trien khai co che huy hop tac
- Su dung Flow cho cac luong du lieu phan ung
- Uu tien StateFlow cho trang thai UI, SharedFlow cho su kien
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Viec lam chu coroutines se mang lai loi the dang ke trong cac du an Android va phong van ky thuat. Hay thuc hanh thuong xuyen va kham pha cac pattern nang cao.
Thẻ
Chia sẻ
Bài viết liên quan

Kotlin 2.3 cho Android: Name-Based Destructuring, KMP và Câu Hỏi Phỏng Vấn 2026
Câu hỏi phỏng vấn Kotlin 2.3 dành cho lập trình viên Android năm 2026. Name-based destructuring, KMP, context parameters, Flow và coroutines kèm ví dụ code.

Jetpack Compose: Hoạt ảnh nâng cao từng bước
Hướng dẫn đầy đủ về hoạt ảnh nâng cao trong Compose: chuyển tiếp, AnimatedVisibility, Animatable, cử chỉ và hiệu năng cho giao diện Android mượt mà.

20 Câu Hỏi Phỏng Vấn Jetpack Compose Hàng Đầu Năm 2026
20 câu hỏi phỏng vấn Jetpack Compose thường gặp nhất: recomposition, quản lý state, navigation, hiệu năng và các pattern kiến trúc với ví dụ code chi tiết.