kotlin
156 lines · 2 tabs
Alex Chen
Jan 2026
2 tabs
package com.example.myapp.workers
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.*
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import java.util.concurrent.TimeUnit
@HiltWorker
class SyncWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val repository: PostRepository
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
// Get input data
val forceRefresh = inputData.getBoolean(KEY_FORCE_REFRESH, false)
setProgress(workDataOf(PROGRESS to 0))
repository.syncPosts(forceRefresh)
setProgress(workDataOf(PROGRESS to 100))
// Return success with output data
Result.success(
workDataOf(
KEY_SYNC_TIME to System.currentTimeMillis()
)
)
} catch (e: Exception) {
if (runAttemptCount < 3) {
Result.retry()
} else {
Result.failure(
workDataOf(KEY_ERROR to e.message)
)
}
}
}
companion object {
const val KEY_FORCE_REFRESH = "force_refresh"
const val KEY_SYNC_TIME = "sync_time"
const val KEY_ERROR = "error"
const val PROGRESS = "progress"
fun schedulePeriodicSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
15, TimeUnit.MINUTES
)
.setConstraints(constraints)
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
10, TimeUnit.SECONDS
)
.build()
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
"sync_posts",
ExistingPeriodicWorkPolicy.KEEP,
syncRequest
)
}
fun scheduleOneTimeSync(
context: Context,
forceRefresh: Boolean = false
) {
val inputData = workDataOf(KEY_FORCE_REFRESH to forceRefresh)
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
.setInputData(inputData)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(
"sync_posts_once",
ExistingWorkPolicy.REPLACE,
syncRequest
)
}
}
}
package com.example.myapp.workers
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@HiltViewModel
class SyncViewModel @Inject constructor(
private val workManager: WorkManager
) : ViewModel() {
val syncStatus: StateFlow<SyncStatus> =
workManager.getWorkInfosForUniqueWorkFlow("sync_posts")
.map { workInfos ->
workInfos.firstOrNull()?.let { workInfo ->
when (workInfo.state) {
WorkInfo.State.RUNNING -> {
val progress = workInfo.progress.getInt(SyncWorker.PROGRESS, 0)
SyncStatus.Running(progress)
}
WorkInfo.State.SUCCEEDED -> {
val syncTime = workInfo.outputData.getLong(
SyncWorker.KEY_SYNC_TIME, 0
)
SyncStatus.Success(syncTime)
}
WorkInfo.State.FAILED -> {
val error = workInfo.outputData.getString(SyncWorker.KEY_ERROR)
SyncStatus.Failed(error)
}
else -> SyncStatus.Idle
}
} ?: SyncStatus.Idle
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = SyncStatus.Idle
)
fun triggerSync(forceRefresh: Boolean = false) {
SyncWorker.scheduleOneTimeSync(
workManager.configuration.executor as Context,
forceRefresh
)
}
fun cancelSync() {
workManager.cancelUniqueWork("sync_posts")
}
}
sealed class SyncStatus {
object Idle : SyncStatus()
data class Running(val progress: Int) : SyncStatus()
data class Success(val timestamp: Long) : SyncStatus()
data class Failed(val error: String?) : SyncStatus()
}
2 files · kotlin
Explain with highlit
WorkManager schedules deferrable, guaranteed background work. I create Worker or CoroutineWorker subclasses with doWork() method. Constraints specify when work runs—network type, charging status, battery level. One-time requests use OneTimeWorkRequest, periodic work uses PeriodicWorkRequest with minimum 15-minute intervals. Chaining enables dependent tasks with then() and combine(). Work survives app restarts and device reboots. Data objects pass parameters in/out. I observe work status with WorkInfo LiveData. Unique work ensures single instances with conflict policies. WorkManager handles scheduling across API levels, using JobScheduler, AlarmManager, or BroadcastReceiver based on device capabilities.
Related snips
kotlin
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
kotlin
android
hilt
by Alex Chen
3 tabs
kotlin
package com.example.myapp.ui
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class UserViewModel(
LiveData transformations and mediators
kotlin
android
livedata
by Alex Chen
1 tab
kotlin
package com.example.myapp.utils
import android.os.Build
import android.os.StrictMode
import android.os.Trace
import timber.log.Timber
Performance optimization and profiling
kotlin
android
performance
by Alex Chen
2 tabs
kotlin
package com.example.myapp.widget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
App widgets for home screen
kotlin
android
widget
by Alex Chen
2 tabs
kotlin
package com.example.myapp.data.repository
import com.example.myapp.data.local.PostDao
import com.example.myapp.data.local.PostEntity
import com.example.myapp.data.remote.ApiService
import com.example.myapp.models.Post
Unit testing with JUnit and MockK
kotlin
android
testing
by Alex Chen
2 tabs
kotlin
package com.example.myapp.utils
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.app.PendingIntent
Notification channels and categories
kotlin
android
notifications
by Alex Chen
1 tab
Share this code
Here's the card — post it anywhere.