Android WorkManager 2026: Panduan Lengkap Background Tasks, Constraints, dan Pertanyaan Interview

Pelajari Android WorkManager untuk menjalankan background tasks dengan constraints, chaining, dan CoroutineWorker. Termasuk pertanyaan interview untuk persiapan wawancara kerja Android developer.

Android WorkManager 2026: Panduan Lengkap Background Tasks, Constraints, dan Pertanyaan Interview

Android WorkManager menangani pekerjaan background yang dapat ditunda namun dijamin akan dieksekusi, bahkan setelah aplikasi di-restart atau perangkat di-reboot. Sebagai bagian dari Android Jetpack, WorkManager 2.11.2 (stabil, Maret 2026) menyediakan API terpadu yang bekerja pada API 23+ dengan memilih implementasi terbaik (JobScheduler, AlarmManager, atau Firebase JobDispatcher) berdasarkan level API perangkat.

Kapan menggunakan WorkManager

Gunakan WorkManager untuk tugas yang memerlukan jaminan eksekusi: mengunggah log, menyinkronkan data, memproses gambar. Untuk pekerjaan yang harus segera dilakukan dan tidak perlu bertahan saat proses mati, gunakan Kotlin Coroutines.

Menyiapkan WorkManager 2.11 dalam Proyek Kotlin

Sebelum menulis worker apapun, dependency harus dideklarasikan. WorkManager 2.11+ membutuhkan minSdk 23 dan compileSdk 33.

build.gradle.kts (app module)gradle
dependencies {
    val workVersion = "2.11.2"
    implementation("androidx.work:work-runtime-ktx:$workVersion")
    // Optional: for testing
    androidTestImplementation("androidx.work:work-testing:$workVersion")
}

Artifact work-runtime-ktx menyertakan ekstensi Kotlin dan dukungan Coroutine melalui CoroutineWorker. Tidak diperlukan konfigurasi tambahan untuk penggunaan dasar: WorkManager melakukan inisialisasi sendiri melalui ContentProvider.

Membuat Worker Sederhana dengan doWork()

Kelas Worker meng-override doWork() dan mengembalikan Result. Sistem menjalankan method ini pada background thread.

SyncWorker.ktkotlin
class SyncWorker(
    context: Context,
    params: WorkerParameters
) : Worker(context, params) {

    override fun doWork(): Result {
        // Read input data
        val userId = inputData.getString("user_id") ?: return Result.failure()
        
        return try {
            // Perform sync operation
            val syncService = SyncService.getInstance(applicationContext)
            syncService.syncUserData(userId)
            Result.success()
        } catch (e: IOException) {
            // Retry on network errors
            Result.retry()
        } catch (e: Exception) {
            // Permanent failure
            Result.failure()
        }
    }
}

Terdapat tiga hasil yang mungkin: Result.success() menandakan selesai, Result.failure() menghentikan percobaan ulang, dan Result.retry() menjadwalkan ulang sesuai kebijakan backoff.

Menggunakan CoroutineWorker untuk Suspend Functions

Ketika pekerjaan melibatkan suspend functions (panggilan Retrofit, query Room), CoroutineWorker menghilangkan callback yang bersarang.

UploadWorker.ktkotlin
class UploadWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val imageUri = inputData.getString("image_uri")
            ?: return Result.failure()
        
        // Progress reporting (visible in WorkInfo observers)
        setProgress(workDataOf("status" to "compressing"))
        
        val compressed = ImageCompressor.compress(imageUri)
        
        setProgress(workDataOf("status" to "uploading"))
        
        return try {
            val response = ApiClient.imageService.upload(compressed)
            val outputData = workDataOf("url" to response.imageUrl)
            Result.success(outputData)
        } catch (e: HttpException) {
            if (e.code() in 500..599) Result.retry()
            else Result.failure()
        }
    }
}

CoroutineWorker.doWork() berjalan pada Dispatchers.Default. Untuk mengganti dispatcher, gunakan withContext() di dalam fungsi.

Mendefinisikan Constraints untuk Eksekusi Bersyarat

Constraints mencegah pekerjaan berjalan sampai kondisi terpenuhi. Hal ini menghemat baterai dan menghindari percobaan yang gagal.

ScheduleUpload.ktkotlin
fun scheduleUpload(context: Context, imageUri: String) {
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED) // WiFi only
        .setRequiresBatteryNotLow(true)
        .setRequiresStorageNotLow(true)
        .build()

    val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
        .setConstraints(constraints)
        .setInputData(workDataOf("image_uri" to imageUri))
        .setBackoffCriteria(
            BackoffPolicy.EXPONENTIAL,
            Duration.ofMinutes(1)
        )
        .addTag("upload")
        .build()

    WorkManager.getInstance(context)
        .enqueueUniqueWork(
            "upload_$imageUri",
            ExistingWorkPolicy.KEEP,
            uploadRequest
        )
}

Constraints yang tersedia meliputi jenis jaringan (CONNECTED, UNMETERED, METERED, NOT_ROAMING), level baterai, status pengisian daya, ruang penyimpanan, dan status idle perangkat. WorkManager 2.10+ juga menerima NetworkRequest mentah untuk kontrol jaringan yang lebih detail.

Insight Interview

Pertanyaan interview yang umum adalah kapan menggunakan KEEP vs REPLACE di enqueueUniqueWork. KEEP mengabaikan permintaan baru jika pekerjaan dengan nama yang sama sudah ada, REPLACE membatalkan pekerjaan yang ada dan memulai dari awal. Gunakan KEEP untuk upload dimana duplikasi membuang bandwidth, REPLACE untuk sinkronisasi dimana hanya data terbaru yang penting.

Merangkai Work Requests dengan then() dan combine()

Alur kerja yang kompleks memerlukan eksekusi sekuensial dan paralel. WorkManager merangkai work requests menggunakan beginWith() dan then().

WorkChain.ktkotlin
fun processAndUploadImages(context: Context, imageUris: List<String>) {
    val workManager = WorkManager.getInstance(context)

    // Parallel compression workers
    val compressRequests = imageUris.map { uri ->
        OneTimeWorkRequestBuilder<CompressWorker>()
            .setInputData(workDataOf("uri" to uri))
            .build()
    }

    // Single upload worker runs after all compressions complete
    val uploadRequest = OneTimeWorkRequestBuilder<BatchUploadWorker>()
        .setConstraints(
            Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()
        )
        .build()

    // Cleanup runs after upload, regardless of success
    val cleanupRequest = OneTimeWorkRequestBuilder<CleanupWorker>()
        .build()

    workManager
        .beginWith(compressRequests) // Parallel
        .then(uploadRequest)          // Sequential
        .then(cleanupRequest)          // Sequential
        .enqueue()
}

Output dari parallel workers digabungkan ke dalam ArrayCreatingInputMerger secara default. Worker berikutnya menerima semua pasangan key-value, dengan array yang dibuat untuk key duplikat.

Siap menguasai wawancara Android Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Menjadwalkan Periodic Work dengan PeriodicWorkRequest

PeriodicWorkRequest dieksekusi berulang dengan interval minimum 15 menit (Android memberlakukan batas ini).

PeriodicSync.ktkotlin
fun scheduleDailySync(context: Context) {
    val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
        repeatInterval = 6, 
        repeatIntervalTimeUnit = TimeUnit.HOURS,
        flexTimeWindow = 30,
        flexTimeUnit = TimeUnit.MINUTES
    )
        .setConstraints(
            Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build()
        )
        .addTag("periodic_sync")
        .build()

    WorkManager.getInstance(context)
        .enqueueUniquePeriodicWork(
            "daily_sync",
            ExistingPeriodicWorkPolicy.UPDATE,
            syncRequest
        )
}

Flex window memungkinkan WorkManager untuk mengelompokkan pekerjaan dengan job lain, meningkatkan efisiensi baterai. Interval 6 jam dengan flex 30 menit berarti eksekusi terjadi antara 5:30 dan 6:00 setelah eksekusi sebelumnya.

Mengamati Status Pekerjaan dengan LiveData dan Flow

WorkManager mengekspos status pekerjaan melalui WorkInfo. Amati menggunakan LiveData atau Kotlin Flow.

WorkObserver.ktkotlin
class UploadViewModel(application: Application) : AndroidViewModel(application) {

    private val workManager = WorkManager.getInstance(application)

    // Flow-based observation
    fun observeUpload(workId: UUID): Flow<WorkInfo?> {
        return workManager.getWorkInfoByIdFlow(workId)
    }

    // Check if any upload is running
    val activeUploads: Flow<List<WorkInfo>> = 
        workManager.getWorkInfosByTagFlow("upload")
            .map { workInfos ->
                workInfos.filter { it.state == WorkInfo.State.RUNNING }
            }
}

// In Compose UI
@Composable
fun UploadProgress(workId: UUID, viewModel: UploadViewModel) {
    val workInfo by viewModel.observeUpload(workId)
        .collectAsState(initial = null)

    when (workInfo?.state) {
        WorkInfo.State.RUNNING -> {
            val status = workInfo?.progress?.getString("status") ?: "working"
            CircularProgressIndicator()
            Text(status)
        }
        WorkInfo.State.SUCCEEDED -> {
            val url = workInfo?.outputData?.getString("url")
            Text("Uploaded: $url")
        }
        WorkInfo.State.FAILED -> Text("Upload failed")
        else -> {}
    }
}

Status pekerjaan mengikuti lifecycle: ENQUEUED, RUNNING, SUCCEEDED/FAILED/CANCELLED, BLOCKED (menunggu dependencies).

API Work Metrics WorkManager 2.12

WorkManager 2.12.0-rc01 (Agustus 2026) memperkenalkan WorkMetricsInfo untuk melacak riwayat eksekusi.

MetricsExample.ktkotlin
class MetricsRepository(private val context: Context) {
    
    private val workManager = WorkManager.getInstance(context)

    suspend fun getWorkerMetrics(workerName: String): List<WorkMetricsInfo> {
        val query = WorkMetricsQuery.Builder()
            .setWorkerClassName(workerName)
            .setLimit(100)
            .build()
        
        return workManager.getWorkMetrics(query)
    }

    fun analyzeFailures(metrics: List<WorkMetricsInfo>): Map<Int, Int> {
        // Count stop reasons
        return metrics
            .flatMap { it.stopReasonCounts.entries }
            .groupBy { it.key }
            .mapValues { entry -> entry.value.sumOf { it.value } }
    }
}

Metrics mencakup durasi eksekusi, jumlah retry, alasan berhenti, dan timestamp. API ini memungkinkan debugging kegagalan intermiten di production tanpa infrastruktur logging kustom.

Retensi Metrics

Data WorkMetricsInfo dipangkas setelah 7 hari secara default. Konfigurasi retensi dengan Configuration.Builder().setWorkMetricsRetentionDuration().

Menguji Workers dengan WorkManagerTestInitHelper

Unit testing workers memerlukan artifact work-testing. TestListenableWorkerBuilder membuat workers tanpa meng-enqueue mereka.

SyncWorkerTest.ktkotlin
@RunWith(AndroidJUnit4::class)
class SyncWorkerTest {

    private lateinit var context: Context

    @Before
    fun setup() {
        context = ApplicationProvider.getApplicationContext()
        val config = Configuration.Builder()
            .setMinimumLoggingLevel(Log.DEBUG)
            .setExecutor(SynchronousExecutor())
            .build()
        WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
    }

    @Test
    fun syncWorker_withValidUserId_succeeds() = runTest {
        val inputData = workDataOf("user_id" to "123")
        
        val worker = TestListenableWorkerBuilder<SyncWorker>(context)
            .setInputData(inputData)
            .build()

        val result = worker.doWork()
        
        assertThat(result).isEqualTo(ListenableWorker.Result.success())
    }

    @Test
    fun syncWorker_withoutUserId_fails() = runTest {
        val worker = TestListenableWorkerBuilder<SyncWorker>(context)
            .build()

        val result = worker.doWork()
        
        assertThat(result).isEqualTo(ListenableWorker.Result.failure())
    }
}

Untuk integration tests yang memverifikasi constraints dan chaining, gunakan TestDriver untuk mensimulasikan kepuasan constraint dan perjalanan waktu.

Pertanyaan Interview Umum tentang WorkManager

Interview teknis sering menguji pemahaman tentang jaminan dan trade-off WorkManager. Pertanyaan-pertanyaan ini muncul dalam interview Android level menengah dan senior.

T: Apa yang terjadi pada WorkRequest ketika aplikasi dimatikan?

WorkManager menyimpan work requests dalam database Room. Ketika aplikasi restart atau sistem memberi sinyal bahwa constraint terpenuhi, pekerjaan yang tertunda dilanjutkan. Jaminan ini yang membedakan WorkManager dari pekerjaan CoroutineScope yang mati bersama proses.

T: Bagaimana perbedaan WorkManager dengan AlarmManager?

AlarmManager menjadwalkan alarm dengan waktu yang tepat dan menjalankan kode pada waktu jam tertentu. WorkManager menjadwalkan pekerjaan yang dapat ditunda yang berjalan ketika constraints terpenuhi, tanpa jaminan waktu yang tepat. Gunakan AlarmManager untuk alarm yang menghadap pengguna, WorkManager untuk pemrosesan data background.

T: Bisakah WorkManager menjalankan pekerjaan secara langsung?

Ya, dengan setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST). Expedited work requests menggunakan slot foreground service pada API 31+ atau JobScheduler.setImportantWhileForeground() pada API yang lebih lama. Batas kuota berlaku, jadi OutOfQuotaPolicy mendefinisikan perilaku fallback.

T: Bagaimana cara membatalkan semua pending uploads?

kotlin
WorkManager.getInstance(context).cancelAllWorkByTag("upload")

Tags memungkinkan operasi massal. Alternatifnya, cancelUniqueWork("name") membatalkan chain tertentu.

Menangani System-Induced Stops dengan Backoff Policies

Ketika sistem menghentikan worker (optimisasi baterai, tekanan memori), kebijakan backoff menentukan waktu retry.

BackoffConfig.ktkotlin
val request = OneTimeWorkRequestBuilder<UploadWorker>()
    .setBackoffCriteria(
        BackoffPolicy.EXPONENTIAL,
        Duration.ofSeconds(30) // Initial delay, minimum 10 seconds
    )
    .setBackoffOnSystemInterruption(true) // New in 2.11
    .build()

Exponential backoff menggandakan delay pada setiap retry: 30s, 60s, 120s, hingga maksimum 5 jam. Linear backoff menambahkan delay awal setiap kali: 30s, 60s, 90s. Flag setBackoffOnSystemInterruption() yang baru di WorkManager 2.11 menerapkan backoff bahkan ketika sistem (bukan worker) yang menyebabkan berhenti.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Poin-Poin Penting untuk Penggunaan WorkManager di Production

  • Utamakan CoroutineWorker daripada Worker saat memanggil suspend functions. Ini menghindari pemblokiran thread dan terintegrasi dengan kode coroutine yang ada.
  • Gunakan enqueueUniqueWork() untuk mencegah pekerjaan duplikat. Pilih KEEP untuk operasi idempoten, REPLACE ketika hanya permintaan terbaru yang penting.
  • Tetapkan tag yang bermakna pada setiap request. Tags memungkinkan observasi, pembatalan, dan debugging di seluruh work chains.
  • Uji workers secara terisolasi dengan TestListenableWorkerBuilder, kemudian uji chains dengan simulasi constraint TestDriver.
  • API WorkMetricsInfo WorkManager 2.12 menyediakan riwayat eksekusi. Gunakan untuk debug pola retry dan alasan berhenti di production.
  • Constraints menghemat baterai. Worker yang gagal karena tidak ada jaringan membuang siklus CPU dan menguras daya pada percobaan retry.
  • Minimum 15 menit untuk periodic work diberlakukan oleh Android, bukan WorkManager. Rancang tugas periodik untuk menoleransi interval ini.
Tantangan harian

Bisakah kamu menemukan bug di Android?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 22 Agustus 2026

Bagikan

Artikel terkait