Kotlin Coroutines Rehberi: Android için 2026 Kılavuzu
Android geliştirmede Kotlin coroutines konusunda uzmanlaşın: suspend fonksiyonlar, scope yapıları, dispatcher'lar ve ileri düzey kalıplar.

Kotlin coroutines, Android platformunda asenkron programlamanın temelini yeniden tanımladı. Callback karmaşası ve kullanımdan kaldırılan AsyncTask artık geride kaldı: coroutines sayesinde geliştiriciler, performanslı ve bakımı kolay olan asenkron kodu senkron bir yapıda yazabiliyor.
Coroutines hafiftir (tek bir iş parçacığında binlercesi çalışabilir), doğal olarak iptal edilebilir ve Jetpack ile modern Android ekosistemiyle sorunsuz entegre olur.
Temelleri Anlamak
Koda geçmeden önce, coroutines'i bu kadar güçlü kılan yapıyı anlamak gerekir.
Coroutine Nedir?
Coroutine, askıya alınabilir bir hesaplama örneğidir. İş parçacıklarından farklı olarak coroutines bloklamaz: yürütmeyi askıya alır ve iş parçacığını diğer görevler için serbest bırakır.
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L) // Suspend without blocking
println("World!")
}
println("Hello,")
}
// Output: Hello, World!Suspend Fonksiyonlar: Coroutines'in Kalbi
suspend anahtar kelimesi, bir fonksiyonun iş parçacığını bloklamadan coroutine yürütmesini askıya alabileceğini belirtir.
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)
}Altın kural: Bir suspend fonksiyon yalnızca başka bir suspend fonksiyondan veya bir coroutine kapsamından çağrılabilir.
Coroutine Scope Yapıları
Scope, coroutines'in yaşam döngüsünü belirler. Bellek sızıntılarını önlemek için kritik öneme sahiptir.
viewModelScope: ViewModel'ler için Kapsam
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: Activity ve Fragment'lar için
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)
}
}
}
}
}
}Bir Android uygulamasında asla GlobalScope kullanılmamalıdır. GlobalScope ile başlatılan coroutines'ler herhangi bir yaşam döngüsüne bağlı değildir ve bellek sızıntılarına neden olabilir.
Dispatchers: Yürütme Kontrolü
Dispatcher'lar, coroutine'in hangi iş parçacığında çalışacağını belirler.
4 Temel Dispatcher
// 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 Değiştirme
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 ile Paralel Yürütme
Görevleri paralel olarak yürütmek ve sonuçları birleştirmek için async kullanılır.
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 ile 3 çağrının tümü paralel olarak yürütülür. Her çağrı 1 saniye sürerse, toplam süre sıralı yürütmedeki 3 saniye yerine yaklaşık 1 saniye olur.
Hata Yönetimi
Klasik try/catch
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 Kalıbı
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)
}
}Android mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
İptal: Kaynakları Doğru Temizleme
Coroutines, iş birlikçi iptal mekanizmasını destekler. Kaynak sızıntılarından kaçınmak için bu durum oldukça önemlidir.
Scope ile Otomatik İptal
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
}
}
}İptal Kontrolü
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()
}CancellationException asla yutulmamalıdır: Exception yakalanırken CancellationException yeniden fırlatılmalıdır, böylece iptal mekanizması doğru şekilde yayılır.
Flow: Reaktif Programlama
Flow, RxJava Observable yapısının coroutines karşılığıdır ancak daha sade ve entegre bir çözüm sunar.
Flow Oluşturma ve Toplama
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 ve SharedFlow Karşılaştırması
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()
}Temel Flow Operatörleri
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()
)İleri Düzey Kalıplar
Üstel Geri Çekilme ile Yeniden Deneme
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)
}Zaman Aşımı
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()
} ?: defaultValueSonuc
Kotlin coroutines, modern Android geliştirmenin vazgeçilmez bir parçası haline geldi. Jetpack ekosistemiyle tam uyumlu olan coroutines, asenkron programlamaya zarif ve yüksek performanslı bir yaklaşım sunmaktadır.
Kontrol Listesi
- Sızıntılardan kaçınmak için
viewModelScopevelifecycleScopekullanılmalıdır - Doğru Dispatcher seçilmelidir (Main, IO, Default)
- Hatalar try/catch veya Result wrapper ile ele alınmalıdır
- İş birlikçi iptal mekanizması uygulanmalıdır
- Reaktif veri akışları için Flow kullanılmalıdır
- UI durumu için StateFlow, olaylar için SharedFlow tercih edilmelidir
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Coroutines konusunda uzmanlaşmak, Android projelerinde ve teknik mülakatlarda önemli bir avantaj sağlayacaktır. Düzenli pratik yapmak ve ileri düzey kalıpları keşfetmek, bu alandaki yetkinliği daha da artıracaktır.
Etiketler
Paylaş
İlgili makaleler

Jetpack Compose: Adım Adım İleri Düzey Animasyonlar
Compose'da ileri düzey animasyonlar için kapsamlı rehber: geçişler, AnimatedVisibility, Animatable, hareketler ve akıcı Android arayüzleri için performans.

2026 Yilinda En Cok Sorulan 20 Jetpack Compose Mulakat Sorusu
En sik sorulan 20 Jetpack Compose mulakat sorusu: recomposition, state yonetimi, navigasyon, performans ve mimari desenler.

Android'de MVVM vs MVI: 2026'da Hangi Mimariyi Seçmeli?
Android'de MVVM ve MVI karşılaştırması: avantajlar, kısıtlamalar, kullanım senaryoları ve 2026'da doğru mimariyi seçmek için pratik rehber.