# Menguasai Kotlin Coroutines: Panduan Lengkap 2026 > Pelajari Kotlin coroutines untuk pengembangan Android: suspend function, scope, dispatcher, dan pola-pola tingkat lanjut. - Published: 2026-01-04 - Updated: 2026-04-02 - Author: Anthony Fillion-Maillet - Tags: kotlin, coroutines, android, async, concurrency - Reading time: 12 min --- Kotlin coroutines telah mengubah secara mendasar cara pemrograman asinkron dilakukan di platform Android. Masa-masa callback hell dan AsyncTask yang sudah usang telah berlalu: dengan coroutines, pengembang dapat menulis kode asinkron yang tampak seperti kode sinkron, namun tetap berkinerja tinggi dan mudah dipelihara. > **Mengapa coroutines?** > > Coroutines bersifat ringan (ribuan dapat berjalan dalam satu thread), secara bawaan mendukung pembatalan, dan terintegrasi dengan mulus bersama Jetpack serta ekosistem Android modern. ## Memahami Dasar-Dasar Sebelum masuk ke kode, penting untuk memahami apa yang membuat coroutines begitu powerful. ### Apa Itu Coroutine? Coroutine adalah sebuah instance dari komputasi yang dapat ditangguhkan. Berbeda dengan thread, coroutine tidak memblokir: ia menangguhkan eksekusi dan membebaskan thread untuk tugas-tugas lain. ```kotlin // BasicCoroutine.kt import kotlinx.coroutines.* fun main() = runBlocking { launch { delay(1000L) // Suspend without blocking println("World!") } println("Hello,") } // Output: Hello, World! ``` ### Suspend Function: Inti dari Coroutines Kata kunci `suspend` menunjukkan bahwa sebuah fungsi dapat menangguhkan eksekusi coroutine tanpa memblokir 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) } ``` > **Aturan utama**: Sebuah suspend function hanya dapat dipanggil dari suspend function lain atau dari dalam sebuah coroutine. ## Coroutine Scope Scope menentukan siklus hidup coroutine. Hal ini sangat penting untuk mencegah kebocoran memori. ### viewModelScope: Scope untuk 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: Untuk Activity dan 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) } } } } } } ``` > **Waspadai kebocoran** > > Jangan pernah menggunakan `GlobalScope` dalam aplikasi Android. Coroutine yang diluncurkan dengan `GlobalScope` tidak terikat pada siklus hidup apa pun dan dapat menyebabkan kebocoran memori. ## Dispatcher: Mengontrol Eksekusi Dispatcher menentukan thread mana yang akan digunakan oleh coroutine untuk berjalan. ### 4 Dispatcher Utama ```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: Berpindah 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) } ``` ## Eksekusi Paralel dengan async/await Untuk menjalankan tugas secara paralel dan menggabungkan hasilnya, gunakan `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() ) } } ``` > **Performa** > > Dengan `async`, ketiga pemanggilan dijalankan secara paralel. Jika setiap pemanggilan membutuhkan waktu 1 detik, total waktu yang diperlukan adalah sekitar 1 detik, bukan 3 detik jika dijalankan secara berurutan. ## Penanganan Error ### Klasik 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) } } } ``` ### Pola Result Wrapper ```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) } } ``` ## Pembatalan: Membersihkan Sumber Daya dengan Benar Coroutine mendukung pembatalan kooperatif. Hal ini sangat penting untuk menghindari kebocoran sumber daya. ### Pembatalan Otomatis dengan Scope ```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 } } } ``` ### Memeriksa Pembatalan ```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() } ``` > **Jangan pernah menelan CancellationException**: jika menangkap `Exception`, pastikan untuk melempar kembali `CancellationException` agar pembatalan dapat menyebar dengan benar. ## Flow: Pemrograman Reaktif Flow adalah padanan RxJava Observable dalam dunia coroutines, tetapi lebih sederhana dan terintegrasi secara bawaan. ### Membuat dan Mengumpulkan 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 vs 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() } ``` ### Operator Flow yang Penting ```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() ) ``` ## Pola Tingkat Lanjut ### Retry dengan 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 ``` ## Kesimpulan Kotlin coroutines telah menjadi bagian yang tidak terpisahkan dari pengembangan Android modern. Coroutines menawarkan pendekatan yang elegan dan berkinerja tinggi terhadap pemrograman asinkron, serta terintegrasi dengan sempurna dalam ekosistem Jetpack. ### Daftar Periksa - Gunakan `viewModelScope` dan `lifecycleScope` untuk menghindari kebocoran memori - Pilih Dispatcher yang tepat (Main, IO, Default) - Tangani error dengan try/catch atau Result wrapper - Implementasikan pembatalan kooperatif - Gunakan Flow untuk aliran data reaktif - Utamakan StateFlow untuk state UI, SharedFlow untuk event Penguasaan coroutines akan memberikan keunggulan yang signifikan dalam proyek Android maupun wawancara teknis. Latihan secara berkala dan eksplorasi pola-pola tingkat lanjut akan semakin memperdalam keahlian di bidang ini. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/android/mastering-kotlin-coroutines