Coroutines trong Android
1. Giới thiệu
Kotlin Coroutines giúp viết async code như synchronous code, tránh callback hell.
2. Suspend Functions
// Suspend function có thể pause và resume
suspend fun fetchUser(id: Int): User {
delay(1000) // Non-blocking delay
return api.getUser(id)
}
// Chỉ gọi được từ coroutine hoặc suspend function khác
viewModelScope.launch {
val user = fetchUser(123)
updateUI(user)
}3. Coroutine Builders
launch
// Fire-and-forget, không return value
viewModelScope.launch {
val users = repository.getUsers()
_uiState.value = users
}async
// Return Deferred, await để lấy kết quả
viewModelScope.launch {
val deferredUser = async { api.getUser(1) }
val deferredPosts = async { api.getPosts(1) }
// Parallel execution
val user = deferredUser.await()
val posts = deferredPosts.await()
_uiState.value = UserWithPosts(user, posts)
}runBlocking
// Block thread cho đến khi hoàn thành (chỉ dùng cho testing/main)
fun main() = runBlocking {
val result = fetchData()
}4. Dispatchers
viewModelScope.launch {
// Default: CPU-intensive work
val computed = withContext(Dispatchers.Default) {
heavyComputation()
}
// IO: Network, disk operations
val data = withContext(Dispatchers.IO) {
api.fetchData()
}
// Main: UI updates
withContext(Dispatchers.Main) {
updateUI(data)
}
}5. Scopes trong Android
viewModelScope
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// Tự động cancel khi ViewModel cleared
val data = repository.getData()
}
}
}lifecycleScope
class MyActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// Tự động cancel khi Activity destroyed
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
}
}6. Exception Handling
try-catch
viewModelScope.launch {
try {
val data = api.fetchData()
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message)
}
}CoroutineExceptionHandler
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
_uiState.value = UiState.Error(exception.message)
}
viewModelScope.launch(exceptionHandler) {
val data = api.fetchData() // Exception sẽ được handle
}supervisorScope
// Child failure không cancel parent
supervisorScope {
val job1 = launch { task1() } // Failure không ảnh hưởng job2
val job2 = launch { task2() }
}7. Cancellation
class MyViewModel : ViewModel() {
private var fetchJob: Job? = null
fun search(query: String) {
fetchJob?.cancel() // Cancel previous search
fetchJob = viewModelScope.launch {
delay(300) // Debounce
val results = api.search(query)
_results.value = results
}
}
}Cooperative Cancellation
suspend fun fetchAllPages() {
for (page in 1..100) {
ensureActive() // Check cancellation
val data = api.fetchPage(page)
processData(data)
}
}8. withTimeout
viewModelScope.launch {
try {
val result = withTimeout(5000) {
api.slowRequest()
}
} catch (e: TimeoutCancellationException) {
showError("Request timed out")
}
}
// Or with null instead of exception
val result = withTimeoutOrNull(5000) {
api.slowRequest()
} ?: defaultValue9. Parallel vs Sequential
// Sequential: 2 giây
suspend fun sequential() {
val user = api.getUser(1) // 1 giây
val posts = api.getPosts(1) // 1 giây
}
// Parallel: 1 giây
suspend fun parallel() = coroutineScope {
val user = async { api.getUser(1) }
val posts = async { api.getPosts(1) }
UserWithPosts(user.await(), posts.await())
}10. Best Practices
// ✅ Good: Inject dispatcher
class UserRepository(
private val api: ApiService,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
suspend fun getUsers() = withContext(ioDispatcher) {
api.getUsers()
}
}
// ✅ Good: Use viewModelScope
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch { ... }
}
}
// ❌ Bad: GlobalScope
GlobalScope.launch {
// Không được quản lý lifecycle
}
// ❌ Bad: Blocking main thread
runBlocking {
// Đừng dùng trong production code
}📝 Tóm tắt
| Concept | Mô tả |
|---|---|
| suspend | Function có thể pause |
| launch | Fire-and-forget |
| async | Return Deferred |
| withContext | Switch dispatcher |
| viewModelScope | ViewModel lifecycle |
| lifecycleScope | Activity/Fragment lifecycle |
| Dispatchers.IO | Network/disk |
| Dispatchers.Main | UI thread |
Last updated on