Skip to content

Latest commit

 

History

History
1084 lines (766 loc) · 22.7 KB

File metadata and controls

1084 lines (766 loc) · 22.7 KB

API Reference

Complete API documentation for KMP WorkManager.

Table of Contents


BackgroundTaskScheduler

The main interface for scheduling and managing background tasks.

Methods

enqueue()

Schedule a single background task.

suspend fun enqueue(
    id: String,
    trigger: TaskTrigger,
    workerClassName: String,
    input: String? = null,
    constraints: Constraints = Constraints()
): ScheduleResult

Parameters:

  • id: String - Unique identifier for the task. If a task with the same ID exists, behavior depends on ExistingWorkPolicy in constraints.
  • trigger: TaskTrigger - When and how the task should be executed (OneTime, Periodic, Exact, etc.)
  • workerClassName: String - Name of the worker class that will execute the task
  • input: String? - Optional input data passed to the worker (must be serializable)
  • constraints: Constraints - Execution constraints (network, battery, charging, etc.)

Returns: ScheduleResult - Result of the scheduling operation

Example:

val result = scheduler.enqueue(
    id = "data-sync",
    trigger = TaskTrigger.Periodic(intervalMs = 15_MINUTES),
    workerClassName = "SyncWorker",
    constraints = Constraints(requiresNetwork = true)
)

beginWith()

Start building a task chain with a single task or multiple parallel tasks.

fun beginWith(request: TaskRequest): TaskChain

fun beginWith(requests: List<TaskRequest>): TaskChain

Parameters:

  • request: TaskRequest - Single task to start the chain
  • requests: List<TaskRequest> - Multiple tasks to run in parallel at the start

Returns: TaskChain - Builder for constructing task chains

Example:

// Sequential chain
scheduler.beginWith(TaskRequest(workerClassName = "DownloadWorker"))
    .then(TaskRequest(workerClassName = "ProcessWorker"))
    .enqueue()

// Parallel start
scheduler.beginWith(listOf(
    TaskRequest(workerClassName = "SyncWorker"),
    TaskRequest(workerClassName = "CacheWorker")
))
    .then(TaskRequest(workerClassName = "FinalizeWorker"))
    .enqueue()

cancel()

Cancel a specific task by its ID.

suspend fun cancel(id: String)

Parameters:

  • id: String - ID of the task to cancel

Example:

scheduler.cancel("data-sync")

cancelAll()

Cancel all scheduled tasks.

suspend fun cancelAll()

Example:

scheduler.cancelAll()

WorkerResult (v2.3.0+)

New in v2.3.0: Workers can now return structured results instead of just boolean.

WorkerResult Sealed Class

sealed class WorkerResult {
    data class Success(
        val message: String? = null,
        val data: Map<String, Any?>? = null
    ) : WorkerResult()

    data class Failure(
        val message: String
    ) : WorkerResult()
}

Worker Interface

Common Worker:

interface CommonWorker {
    suspend fun doWork(input: String?): WorkerResult
}

Backward Compatibility:

Workers returning Boolean are automatically converted to WorkerResult:

  • trueWorkerResult.Success()
  • falseWorkerResult.Failure("Task failed")

Examples

Basic Success/Failure

class SyncWorker : CommonWorker {
    override suspend fun doWork(input: String?): WorkerResult {
        return try {
            syncData()
            WorkerResult.Success(message = "Sync completed")
        } catch (e: Exception) {
            WorkerResult.Failure("Sync failed: ${e.message}")
        }
    }
}

Returning Data

class DownloadWorker : CommonWorker {
    override suspend fun doWork(input: String?): WorkerResult {
        val config = Json.decodeFromString<DownloadConfig>(input!!)
        val file = downloadFile(config.url, config.savePath)

        return WorkerResult.Success(
            message = "Downloaded ${file.length()} bytes in 5s",
            data = mapOf(
                "filePath" to config.savePath,
                "fileSize" to file.length(),
                "url" to config.url,
                "duration" to 5000L
            )
        )
    }
}

Handling Results

// In your application code
when (val result = worker.doWork(input)) {
    is WorkerResult.Success -> {
        println("Success: ${result.message}")
        val fileSize = result.data?.get("fileSize") as? Long
        println("File size: $fileSize bytes")
    }
    is WorkerResult.Failure -> {
        println("Failed: ${result.message}")
    }
}

Data Passing in Chains

// Worker 1: Download file and return metadata
class DownloadWorker : CommonWorker {
    override suspend fun doWork(input: String?): WorkerResult {
        val file = download(url)
        return WorkerResult.Success(
            data = mapOf("filePath" to file.path, "size" to file.size)
        )
    }
}

// Worker 2: Process downloaded file
class ProcessWorker : CommonWorker {
    override suspend fun doWork(input: String?): WorkerResult {
        // In v2.3.0: Access previous worker data via event bus or custom implementation
        // In v2.4.0: Automatic data passing will be supported
        return WorkerResult.Success(message = "Processed file")
    }
}

// Chain them together
scheduler.beginWith(TaskRequest("DownloadWorker"))
    .then(TaskRequest("ProcessWorker"))
    .withId("download-process-chain", policy = ExistingPolicy.KEEP)
    .enqueue()

Benefits

Structured Data Return: Return any data from workers ✅ Better Error Messages: Detailed failure messages ✅ Type Safety: Explicit success/failure handling ✅ Backward Compatible: Boolean returns still work ✅ Built-in Workers: All 5 built-in workers return meaningful data


Task Triggers

Task triggers define when and how tasks should be executed.

TaskTrigger.OneTime

Execute a task once after an optional delay.

data class OneTime(
    val initialDelayMs: Long = 0
) : TaskTrigger

Parameters:

  • initialDelayMs: Long - Delay before execution in milliseconds (default: 0)

Supported Platforms: Android, iOS

Example:

TaskTrigger.OneTime(initialDelayMs = 5_000) // Execute after 5 seconds

TaskTrigger.Periodic

Execute a task repeatedly at fixed intervals.

data class Periodic(
    val intervalMs: Long,
    val flexMs: Long? = null
) : TaskTrigger

Parameters:

  • intervalMs: Long - Interval between executions in milliseconds (minimum: 15 minutes)
  • flexMs: Long? - Flex time window for Android WorkManager (optional)

Supported Platforms: Android, iOS

Important Notes:

  • Android: Minimum interval is 15 minutes (enforced by WorkManager)
  • iOS: Task automatically re-schedules after completion
  • iOS: Actual execution time determined by BGTaskScheduler (opportunistic)

Example:

TaskTrigger.Periodic(
    intervalMs = 30 * 60 * 1000, // 30 minutes
    flexMs = 5 * 60 * 1000       // 5 minutes flex
)

TaskTrigger.Exact

Execute a task at a precise time.

data class Exact(
    val atEpochMillis: Long
) : TaskTrigger

Parameters:

  • atEpochMillis: Long - Exact timestamp in epoch milliseconds

Supported Platforms: Android, iOS

Implementation:

  • Android: Uses AlarmManager.setExactAndAllowWhileIdle()
  • iOS: Uses UNUserNotificationCenter local notifications

Example:

val targetTime = Clock.System.now()
    .plus(1.hours)
    .toEpochMilliseconds()

TaskTrigger.Exact(atEpochMillis = targetTime)

TaskTrigger.Windowed

Execute a task within a time window.

data class Windowed(
    val startEpochMillis: Long,
    val endEpochMillis: Long
) : TaskTrigger

Parameters:

  • startEpochMillis: Long - Window start time in epoch milliseconds
  • endEpochMillis: Long - Window end time in epoch milliseconds

Supported Platforms: Android only (iOS returns REJECTED_OS_POLICY)

Example:

val now = Clock.System.now().toEpochMilliseconds()
TaskTrigger.Windowed(
    startEpochMillis = now + 60_000,      // Start in 1 minute
    endEpochMillis = now + 5 * 60_000     // End in 5 minutes
)

TaskTrigger.ContentUri

Trigger a task when content provider changes are detected.

data class ContentUri(
    val uriString: String,
    val triggerForDescendants: Boolean = true
) : TaskTrigger

Parameters:

  • uriString: String - Content URI to observe (e.g., "content://media/external/images/media")
  • triggerForDescendants: Boolean - Whether to trigger for descendant URIs (default: true)

Supported Platforms: Android only (iOS returns REJECTED_OS_POLICY)

Example:

TaskTrigger.ContentUri(
    uriString = "content://media/external/images/media",
    triggerForDescendants = true
)

System State Triggers

Trigger tasks based on device state changes.

data object BatteryLow : TaskTrigger
data object BatteryOkay : TaskTrigger
data object StorageLow : TaskTrigger
data object DeviceIdle : TaskTrigger

Supported Platforms:

  • BatteryLow, BatteryOkay: Android, iOS
  • StorageLow, DeviceIdle: Android only

Example:

// Run heavy processing when battery is good
scheduler.enqueue(
    id = "ml-training",
    trigger = TaskTrigger.BatteryOkay,
    workerClassName = "MLTrainingWorker",
    constraints = Constraints(requiresCharging = true)
)

Constraints

Constraints define the conditions under which a task can run.

data class Constraints(
    // Network
    val requiresNetwork: Boolean = false,
    val networkType: NetworkType = NetworkType.CONNECTED,
    val requiresUnmeteredNetwork: Boolean = false,

    // Battery
    val requiresCharging: Boolean = false,
    val requiresBatteryNotLow: Boolean = false,

    // Storage
    val requiresStorageNotLow: Boolean = false,

    // Device State
    val requiresDeviceIdle: Boolean = false,
    val allowWhileIdle: Boolean = false,

    // Task Properties
    val isHeavyTask: Boolean = false,
    val expedited: Boolean = false,

    // Retry Policy
    val backoffPolicy: BackoffPolicy = BackoffPolicy.EXPONENTIAL,
    val backoffDelayMs: Long = 10_000,

    // Existing Work Policy
    val existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.KEEP,

    // iOS Quality of Service
    val qos: QualityOfService = QualityOfService.DEFAULT
)

Network Constraints

requiresNetwork: Boolean = false

Whether the task requires network connectivity.

Platforms: Android, iOS


networkType: NetworkType = NetworkType.CONNECTED

Type of network required. Options:

  • NetworkType.NOT_REQUIRED - No network needed
  • NetworkType.CONNECTED - Any network connection
  • NetworkType.UNMETERED - WiFi or unlimited data (Android only)
  • NetworkType.NOT_ROAMING - Non-roaming network (Android only)
  • NetworkType.METERED - Cellular data allowed (Android only)
  • NetworkType.TEMPORARILY_UNMETERED - Temporarily free network (Android only)

Platforms: Android (full support), iOS (only CONNECTED/NOT_REQUIRED)


requiresUnmeteredNetwork: Boolean = false

Shortcut for requiring WiFi (same as networkType = NetworkType.UNMETERED).

Platforms: Android only


Battery Constraints

requiresCharging: Boolean = false

Whether the device must be charging.

Platforms: Android, iOS


requiresBatteryNotLow: Boolean = false

Whether the battery level must be above the low threshold.

Platforms: Android, iOS


Storage Constraints

requiresStorageNotLow: Boolean = false

Whether the device must have sufficient storage available.

Platforms: Android only


Device State Constraints

requiresDeviceIdle: Boolean = false

Whether the device must be idle (screen off, not recently used).

Platforms: Android only


allowWhileIdle: Boolean = false

Whether the task can run while the device is in Doze mode.

Platforms: Android only


Task Property Constraints

isHeavyTask: Boolean = false

Whether this is a long-running task (>10 minutes).

  • Android: Uses KmpHeavyWorker with foreground service
  • iOS: Uses BGProcessingTask instead of BGAppRefreshTask

Platforms: Android, iOS


expedited: Boolean = false

Whether the task should be expedited (run as soon as possible).

Platforms: Android only (uses expedited WorkManager jobs)


Retry Policy

backoffPolicy: BackoffPolicy = BackoffPolicy.EXPONENTIAL

Retry strategy when a task fails. Options:

  • BackoffPolicy.EXPONENTIAL - Exponential backoff (10s, 20s, 40s, 80s, ...)
  • BackoffPolicy.LINEAR - Linear backoff (10s, 20s, 30s, 40s, ...)

Platforms: Android, iOS


backoffDelayMs: Long = 10_000

Initial backoff delay in milliseconds (default: 10 seconds).

Platforms: Android, iOS


Existing Work Policy

existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.KEEP

What to do when a task with the same ID already exists. Options:

  • ExistingWorkPolicy.REPLACE - Cancel existing and schedule new task
  • ExistingWorkPolicy.KEEP - Keep existing task, ignore new request
  • ExistingWorkPolicy.APPEND - Queue new task after existing one
  • ExistingWorkPolicy.APPEND_OR_REPLACE - Append if existing is running, replace otherwise

Platforms: Android (full support), iOS (only REPLACE/KEEP)


iOS Quality of Service

qos: QualityOfService = QualityOfService.DEFAULT

iOS priority hint for task execution. Options:

  • QualityOfService.HIGH - User-initiated priority
  • QualityOfService.DEFAULT - Default priority
  • QualityOfService.LOW - Background priority

Platforms: iOS only


TaskChain

Builder for creating sequential and parallel task workflows.

Methods

then()

Add the next step to the chain.

fun then(request: TaskRequest): TaskChain

fun then(requests: List<TaskRequest>): TaskChain

Parameters:

  • request: TaskRequest - Single task to execute next
  • requests: List<TaskRequest> - Multiple tasks to run in parallel

Returns: TaskChain - The chain builder for further chaining


withId() (v2.3.0+)

Set a unique ID for the chain and specify the ExistingPolicy.

fun withId(
    id: String,
    policy: ExistingPolicy = ExistingPolicy.REPLACE
): TaskChain

Parameters:

  • id: String - Unique identifier for the chain
  • policy: ExistingPolicy - How to handle if a chain with this ID already exists
    • ExistingPolicy.KEEP - Skip if chain already running
    • ExistingPolicy.REPLACE - Cancel old chain and start new one

Returns: TaskChain - New chain instance with the specified ID and policy

Example:

// Prevent duplicate chain execution
scheduler.beginWith(TaskRequest("DownloadWorker"))
    .then(TaskRequest("ProcessWorker"))
    .withId("download-process-workflow", policy = ExistingPolicy.KEEP)
    .enqueue()

// Click button multiple times - only runs once
button.onClick {
    scheduler.beginWith(TaskRequest("SyncWorker"))
        .withId("sync-chain", policy = ExistingPolicy.KEEP)
        .enqueue()
}

enqueue()

Execute the constructed task chain.

fun enqueue()

Note: No return value in v2.3.0. The chain is enqueued asynchronously.


TaskRequest

Data class representing a task in a chain.

data class TaskRequest(
    val id: String = UUID.randomUUID().toString(),
    val workerClassName: String,
    val input: String? = null,
    val constraints: Constraints = Constraints()
)

Parameters:

  • id: String - Unique task identifier (auto-generated if not provided)
  • workerClassName: String - Name of the worker class
  • input: String? - Optional input data
  • constraints: Constraints - Execution constraints

Examples

// Sequential execution
scheduler
    .beginWith(TaskRequest(workerClassName = "DownloadWorker"))
    .then(TaskRequest(workerClassName = "ProcessWorker"))
    .then(TaskRequest(workerClassName = "UploadWorker"))
    .enqueue()

// Parallel execution
scheduler
    .beginWith(listOf(
        TaskRequest(workerClassName = "SyncWorker"),
        TaskRequest(workerClassName = "CacheWorker"),
        TaskRequest(workerClassName = "CleanupWorker")
    ))
    .then(TaskRequest(workerClassName = "FinalizeWorker"))
    .enqueue()

// Mixed sequential and parallel
scheduler
    .beginWith(TaskRequest(workerClassName = "DownloadWorker"))
    .then(listOf(
        TaskRequest(workerClassName = "ProcessImageWorker"),
        TaskRequest(workerClassName = "ProcessVideoWorker")
    ))
    .then(TaskRequest(workerClassName = "UploadWorker"))
    .enqueue()

Events

Event system for worker-to-UI communication.

TaskEventBus

Singleton object for emitting and collecting task completion events.

object TaskEventBus {
    val events: SharedFlow<TaskCompletionEvent>

    suspend fun emit(event: TaskCompletionEvent)
}

TaskCompletionEvent

Event emitted when a task completes.

data class TaskCompletionEvent(
    val taskName: String,
    val success: Boolean,
    val message: String,
    val timestamp: Long = Clock.System.now().toEpochMilliseconds()
)

Parameters:

  • taskName: String - Name of the worker that completed
  • success: Boolean - Whether the task succeeded
  • message: String - Human-readable message
  • timestamp: Long - Event timestamp in epoch milliseconds

Usage

Emitting events from workers:

class SyncWorker : IosWorker {
    override suspend fun doWork(input: String?): Boolean {
        return try {
            syncDataFromServer()

            TaskEventBus.emit(
                TaskCompletionEvent(
                    taskName = "SyncWorker",
                    success = true,
                    message = "✅ Data synced successfully"
                )
            )

            true
        } catch (e: Exception) {
            TaskEventBus.emit(
                TaskCompletionEvent(
                    taskName = "SyncWorker",
                    success = false,
                    message = "❌ Sync failed: ${e.message}"
                )
            )

            false
        }
    }
}

Collecting events in UI:

@Composable
fun TaskMonitor() {
    LaunchedEffect(Unit) {
        TaskEventBus.events.collect { event ->
            when {
                event.success -> {
                    showSuccessToast(event.message)
                }
                else -> {
                    showErrorToast(event.message)
                }
            }
        }
    }
}

Enums

ScheduleResult

Result of a task scheduling operation.

enum class ScheduleResult {
    SUCCESS,                    // Task scheduled successfully
    REJECTED_OS_POLICY,         // OS rejected the task (e.g., iOS background restrictions)
    REJECTED_INVALID_PARAMS,    // Invalid parameters provided
    FAILED_UNKNOWN              // Unknown error occurred
}

BackoffPolicy

Retry strategy for failed tasks.

enum class BackoffPolicy {
    EXPONENTIAL,  // Exponential backoff (10s, 20s, 40s, 80s, ...)
    LINEAR        // Linear backoff (10s, 20s, 30s, 40s, ...)
}

ExistingWorkPolicy

Policy for handling existing tasks with the same ID.

enum class ExistingWorkPolicy {
    REPLACE,            // Cancel existing and schedule new task
    KEEP,              // Keep existing task, ignore new request
    APPEND,            // Queue new task after existing one
    APPEND_OR_REPLACE  // Append if running, replace otherwise
}

NetworkType

Network requirement for tasks.

enum class NetworkType {
    NOT_REQUIRED,           // No network needed
    CONNECTED,              // Any network connection
    UNMETERED,              // WiFi or unlimited data
    NOT_ROAMING,            // Non-roaming network
    METERED,                // Cellular data allowed
    TEMPORARILY_UNMETERED   // Temporarily free network
}

QualityOfService (iOS)

Priority hint for iOS tasks.

enum class QualityOfService {
    HIGH,       // User-initiated priority
    DEFAULT,    // Default priority
    LOW         // Background priority
}

Platform-Specific APIs

Android

KmpWorker

Base worker class for deferrable tasks.

class KmpWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val workerClassName = inputData.getString("workerClassName")

        return when (workerClassName) {
            "YourWorker" -> executeYourWorker()
            else -> Result.failure()
        }
    }

    private suspend fun executeYourWorker(): Result {
        // Your implementation
        return Result.success()
    }
}

KmpHeavyWorker

Foreground service worker for long-running tasks (>10 minutes).

class KmpHeavyWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        setForeground(createForegroundInfo())

        // Your long-running work here

        return Result.success()
    }

    private fun createForegroundInfo(): ForegroundInfo {
        // Create notification for foreground service
    }
}

iOS

IosWorker

Interface for iOS background workers.

interface IosWorker {
    suspend fun doWork(input: String?): Boolean
}

Implementation:

class SyncWorker : IosWorker {
    override suspend fun doWork(input: String?): Boolean {
        // Your implementation (must complete within 25 seconds)
        return true // Return true for success, false for failure
    }
}

IosWorkerFactory

Factory for creating worker instances.

object IosWorkerFactory {
    fun createWorker(className: String): IosWorker? {
        return when (className) {
            "SyncWorker" -> SyncWorker()
            "UploadWorker" -> UploadWorker()
            else -> null
        }
    }
}

Constants

const val ONE_SECOND = 1_000L
const val ONE_MINUTE = 60_000L
const val FIFTEEN_MINUTES = 900_000L
const val ONE_HOUR = 3_600_000L
const val ONE_DAY = 86_400_000L

Need More Help?