Tüm Yazılar
KategoriAndroid
Okuma Süresi
16 dk okuma
Yayın Tarihi
...
Kelime Sayısı
1.063kelime

Kahveni hazırla - bu içerikli bir makale!

WorkManager 2.10 yenilikler: Coroutines-first API, CoroutineWorker, Flow<WorkInfo>, expedited work, foreground service integration ve testing best practices.

WorkManager 2.10 Coroutines: Background Task Modern Yaklaşım

# WorkManager 2.10 Coroutines: Background Task Modern Yaklaşım

WorkManager 2.10 (2024 Q4 stable), Android background task'ların altın standardı. JobScheduler, AlarmManager ve Firebase JobDispatcher için tek replacement API. Coroutines-first yaklaşım, expedited work desteği, Flow<WorkInfo> observation — hepsi mature. Bu yazı WorkManager 2.10'un modern pattern'lerini, Android 15 foreground service restriction'larıyla uyumu, testing stratejisini ve migration best practice'lerini anlatır.

💡 Pro Tip: WorkManager her background task için değil. Network sync, upload, periodic refresh için ideal. Real-time event için değil — Firebase Messaging push + WorkManager kombinasyonu.

İçindekiler


WorkManager Nedir, Ne Zaman

Kullan:

  • Network sync (deferrable, periodic)
  • Image/video upload
  • Database cleanup (haftalık)
  • Analytics batch send
  • Offline queue processing
  • ML model download

Kullanma:

  • Real-time messaging (FCM)
  • Active user interaction (UI thread'de async)
  • Ultra-time-sensitive operations (<30s)
  • Background audio playback (MediaSession)

CoroutineWorker: Modern Worker

kotlin
1import androidx.work.CoroutineWorker
2import androidx.work.WorkerParameters
3import kotlinx.coroutines.Dispatchers
4import kotlinx.coroutines.withContext
5 
6class SyncDataWorker(
7 context: Context,
8 params: WorkerParameters
9) : CoroutineWorker(context, params) {
10 
11 override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
12 try {
13 val data = inputData.getString("user_id")
14 ?: return@withContext Result.failure()
15 
16 // Network call with coroutine
17 val users = userRepository.syncUsers(data)
18 
19 // Persist
20 userDao.insertAll(users)
21 
22 // Return success data
23 val output = workDataOf("synced_count" to users.size)
24 Result.success(output)
25 } catch (e: IOException) {
26 Result.retry() // Exponential backoff
27 } catch (e: Exception) {
28 Result.failure()
29 }
30 }
31}

Enqueue

kotlin
1val syncRequest = OneTimeWorkRequestBuilder()
2 .setInputData(workDataOf("user_id" to userId))
3 .setConstraints(
4 Constraints.Builder()
5 .setRequiredNetworkType(NetworkType.CONNECTED)
6 .setRequiresBatteryNotLow(true)
7 .build()
8 )
9 .setBackoffCriteria(
10 BackoffPolicy.EXPONENTIAL,
11 WorkRequest.MIN_BACKOFF_MILLIS,
12 TimeUnit.MILLISECONDS
13 )
14 .build()
15 
16WorkManager.getInstance(context).enqueue(syncRequest)

Expedited Work (Android 12+)

Kritik user-perceivable task'lar için:

kotlin
1val urgentSync = OneTimeWorkRequestBuilder()
2 .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
3 .build()
4 
5WorkManager.getInstance(context).enqueue(urgentSync)

Expedited work:

  • Battery optimization bypass
  • Doze mode'da çalışabilir (Android 12+)
  • Quota-limited (günlük kotalar)
  • Quota bittiğinde policy'ye göre fallback

Foreground Service Notification

kotlin
1class UploadWorker(ctx: Context, params: WorkerParameters) :
2 CoroutineWorker(ctx, params) {
3 
4 override suspend fun getForegroundInfo(): ForegroundInfo {
5 val notification = NotificationCompat.Builder(applicationContext, "upload_channel")
6 .setContentTitle("Dosya yükleniyor")
7 .setSmallIcon(R.drawable.ic_upload)
8 .setOngoing(true)
9 .build()
10 
11 return ForegroundInfo(
12 NOTIFICATION_ID,
13 notification,
14 ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
15 )
16 }
17 
18 override suspend fun doWork(): Result {
19 setForeground(getForegroundInfo()) // Foreground service başlat
20 // Upload work
21 return Result.success()
22 }
23}

Constraints Sistemi

kotlin
1val constraints = Constraints.Builder()
2 .setRequiredNetworkType(NetworkType.UNMETERED) // Wi-Fi only
3 .setRequiresCharging(true) // Charging
4 .setRequiresBatteryNotLow(true) // Pil > %15
5 .setRequiresDeviceIdle(true) // Doze mode
6 .setRequiresStorageNotLow(true) // Storage yeterli
7 .build()
8 
9val request = OneTimeWorkRequestBuilder()
10 .setConstraints(constraints)
11 .build()

Tüm constraint'ler MET olunca work çalışır. Değilse sessizce bekler.


Work Chaining

Sıralı task'lar:

kotlin
1WorkManager.getInstance(context)
2 .beginWith(compressWork)
3 .then(uploadWork)
4 .then(notifyWork)
5 .enqueue()
6 
7// Veya parallel + sequential karışık
8WorkManager.getInstance(context)
9 .beginWith(listOf(compressImageWork, compressVideoWork)) // parallel
10 .then(uploadWork) // await both, then upload
11 .enqueue()

Data Passing

kotlin
1// Worker 1'in output'ı
2override suspend fun doWork(): Result {
3 val output = workDataOf("compressed_path" to "/tmp/img.jpg")
4 return Result.success(output)
5}
6 
7// Worker 2 input'u automatically worker 1'in output'u
8override suspend fun doWork(): Result {
9 val path = inputData.getString("compressed_path") ?: return Result.failure()
10 // Upload path
11}

Unique Work Policies

Duplicate prevention:

kotlin
1WorkManager.getInstance(context).enqueueUniqueWork(
2 "user_sync_job", // Unique name
3 ExistingWorkPolicy.REPLACE, // KEEP, REPLACE, APPEND, APPEND_OR_REPLACE
4 syncRequest
5)
  • KEEP: Mevcut varsa yeni'yi ignore et
  • REPLACE: Eski'yi cancel + yeni'yi çalıştır
  • APPEND: Sıraya ekle (chain)
  • APPEND_OR_REPLACE: Append, fail olanı replace

Periodic work:

kotlin
1val periodicRequest = PeriodicWorkRequestBuilder(
2 15, TimeUnit.MINUTES, // repeat interval
3 5, TimeUnit.MINUTES // flex period
4).build()
5 
6WorkManager.getInstance(context).enqueueUniquePeriodicWork(
7 "periodic_sync",
8 ExistingPeriodicWorkPolicy.UPDATE, // UPDATE, KEEP
9 periodicRequest
10)

Min interval: 15 dakika.


Flow<WorkInfo> Observer

Modern reactive monitoring:

kotlin
1class SyncViewModel : ViewModel() {
2 val syncState: Flow = WorkManager
3 .getInstance(context)
4 .getWorkInfoByIdFlow(syncRequest.id)
5 
6 fun enqueueSync() = viewModelScope.launch {
7 syncState.collect { workInfo ->
8 when (workInfo.state) {
9 WorkInfo.State.ENQUEUED -> showQueuedUi()
10 WorkInfo.State.RUNNING -> showRunningUi()
11 WorkInfo.State.SUCCEEDED -> {
12 val count = workInfo.outputData.getInt("synced_count", 0)
13 showSuccess(count)
14 }
15 WorkInfo.State.FAILED -> showError()
16 WorkInfo.State.CANCELLED -> showCancelled()
17 else -> {}
18 }
19 }
20 }
21}

Compose UI:

kotlin
1@Composable
2fun SyncScreen(vm: SyncViewModel = viewModel()) {
3 val state by vm.syncState.collectAsState(initial = null)
4 
5 Column {
6 when (state?.state) {
7 WorkInfo.State.RUNNING -> CircularProgressIndicator()
8 WorkInfo.State.SUCCEEDED -> Text("✓ Senkronize edildi")
9 WorkInfo.State.FAILED -> Text("✗ Hata")
10 else -> Button(onClick = { vm.enqueueSync() }) { Text("Sync") }
11 }
12 }
13}

Android 15 Compatibility

Android 15 foreground service strict mode:

  1. setExpedited kullan — Doze bypass için
  2. foregroundServiceType belirt (dataSync, location, etc.)
  3. Manifest'e permission ekle
  4. setForeground çağrısını doWork içinde yap
xml
1
2
3
4 
5
6 android:name="androidx.work.impl.foreground.SystemForegroundService"
7 android:foregroundServiceType="dataSync"
8 tools:node="merge" />

Testing

kotlin
1// Test harness
2@RunWith(AndroidJUnit4::class)
3class SyncWorkerTest {
4 private lateinit var context: Context
5 
6 @Before
7 fun setup() {
8 context = ApplicationProvider.getApplicationContext()
9 val config = Configuration.Builder()
10 .setMinimumLoggingLevel(Log.DEBUG)
11 .setExecutor(SynchronousExecutor())
12 .build()
13 WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
14 }
15 
16 @Test
17 fun testSyncSuccess() = runTest {
18 val worker = TestListenableWorkerBuilder(context)
19 .setInputData(workDataOf("user_id" to "123"))
20 .build()
21 
22 val result = worker.doWork()
23 assertEquals(Result.success(), result)
24 }
25}

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

kotlin
1// WorkManagerModule.kt (Hilt)
2@Module
3@InstallIn(SingletonComponent::class)
4object WorkManagerModule {
5 
6 @Provides
7 @Singleton
8 fun provideWorkManager(
9 @ApplicationContext context: Context
10 ): WorkManager = WorkManager.getInstance(context)
11 
12 @Provides
13 @IntoMap
14 @WorkerKey(SyncDataWorker::class)
15 fun provideSyncWorker(
16 userRepository: UserRepository
17 ): ChildWorkerFactory = object : ChildWorkerFactory {
18 override fun create(context: Context, params: WorkerParameters): ListenableWorker {
19 return SyncDataWorker(context, params, userRepository)
20 }
21 }
22}
23 
24// HiltWorkerFactory setup for DI

Okuyucu Ödülü

Hilt + WorkManager entegrasyonu ile dependency injection temiz. **External Resources:** - [WorkManager 2.10 docs](https://developer.android.com/topic/libraries/architecture/workmanager) - [Expedited work guide](https://developer.android.com/topic/libraries/architecture/workmanager/advanced/expedited-work) - [WorkManager + Hilt](https://developer.android.com/training/dependency-injection/hilt-jetpack#workmanager) - [Testing guide](https://developer.android.com/topic/libraries/architecture/workmanager/how-to/integration-testing) - [Migration from JobScheduler](https://developer.android.com/topic/libraries/architecture/workmanager/migration)

Sonuç

WorkManager 2.10 Android background task'ın olgun, production-ready API'si. Coroutines-first, Flow<WorkInfo> reactive, expedited work kritik ihtiyaçlar için. Android 15 strict mode uyumlu. Migration JobScheduler/AlarmManager'dan 1-2 hafta; ROI yüksek (daha az battery drain, daha güvenilir). Next-gen work scheduling için hala geçerli olan standard.

*İlgili yazılar: Compose 1.7, Kotlin 2.1, Android 15.*

Etiketler

#Android#WorkManager#Kotlin#Coroutines#Background#Jetpack#2026
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz