Skip to main content
This guide helps you install and configure Meeseeks as quickly as possible.
1

Add the dependency

To integrate Meeseeks into your project, add the dependency.
  [versions]
  meeseeks = "0.6.0-alpha02"

  [libraries]
  meeseeks-runtime = { group = "dev.mattramotar.meeseeks", name = "runtime", version.ref = "meeseeks" }
2

(iOS Only) Register BGTaskScheduler Identifiers

In your Info.plist file, add the following:
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
  <string>dev.mattramotar.meeseeks.task.refresh</string>
  <string>dev.mattramotar.meeseeks.task.processing</string>
</array>
3

Initialize Meeseeks

class App: Application(), Configuration.Provider {
  
  val bgTaskManager by lazy { 
    Meeseeks.initialize(applicationContext) {
      minBackoff(20.seconds)
      maxRetryCount(3)
      maxParallelTasks(5)
      allowExpedited()
      register<SyncPayload>(SyncPayload.stableId) { SyncWorker(applicationContext) }
      register<RefreshPayload>(RefreshPayload.stableId) { RefreshWorker(applicationContext) }
    }
  }

  override val workManagerConfiguration: Configuration by lazy {
    val delegating = DelegatingWorkerFactory().apply {
      addFactory(MeeseeksWorkerFactory(bgTaskManager))
    }
    Configuration.Builder().setWorkerFactory(delegating).build()
  }
}
4

(JS Only) Service Worker Setup

If your browser supports Background Sync or Periodic Background Sync, you can handle tasks even when the tab isn’t active.Example service-worker.js:
self.addEventListener('sync', event => {
  if (event.tag && event.tag.startsWith("meeseeks-")) {
    event.waitUntil(handleSync(event.tag));
  }
});

self.addEventListener('periodicsync', event => {
  if (event.tag && event.tag.startsWith("meeseeks-")) {
    event.waitUntil(handleSync(event.tag));
  }
});

async function handleSync(tag) {
  if (self.BGTaskRunner && self.BGTaskRunner.run) {
    await self.BGTaskRunner.run(tag);
  }
};
5

Get the "Meeseeks box"

The BGTaskManager is essentially the MeeseeksBox. It is the global manager of all scheduled tasks. It is what you need to schedule a task (summon a MrMeeseeks).
val bgTaskManager = Meeseeks.initialize(applicationContext) {...}
6

Make the request

bgTaskManager.oneTime(SyncPayload(...)) {
    requireNetwork(true)
    requireCharging(false)
    requireBatteryNotLow(true)
    highPriority()
    retryWithExponentialBackoff(
        initialDelay = 1.minutes,
        maxAttempts = 3
    )
}
7

Meeseeks fulfills the request

taskHandle.observe().collect { status ->
    // Pending -> Running -> Finished
}

Explore advanced usage in the Advanced Docs.

Frequently asked questions

Meeseeks uses a local SQLDelight database to store all task data (including parameters, schedule, status, logs). Each platform has a driver for that database, so your tasks are persisted across app restarts or process deaths.
Because tasks and their metadata live in a local database, Meeseeks can reconstruct and re-schedule them when your app restarts.
  • Android: WorkManager automatically re-schedules tasks after a reboot if you configure it to do so.
  • JVM: Tasks are re-scheduled with Quartz the next time your application launches and re-initializes Meeseeks.
  • Native (iOS): Tasks are re-scheduled with BGTaskScheduler the next time your application launches and re-initializes Meeseeks.
  • JS (Web): If you used SyncManager or PeriodicSyncManager, the browser may continue to run tasks via the ServiceWorker in the background (user permissions and browser support permitting). Otherwise, tasks do not continue to run after the page is closed, but will be re-scheduled when the page is opened again.
  • Android: Concurrency is delegated to WorkManager which manages parallel tasks according to OS constraints.
  • JVM: Concurrency is similarly delegated to Quartz.
  • Native (iOS): Uses BGTaskScheduler for periodic and one-time tasks. Tasks are canceled or updated locally in the DB, and on iOS 16+ can be canceled programmatically via cancelTaskRequestWithIdentifier.
  • JS (Web): Uses ServiceWorker sync/periodicsync if available, otherwise in-memory timers.
You can call MeeseeksBox.sendBackToBox(id) to cancel a task by its MrMeeseeksId. This removes or cancels its scheduled job and updates the database so it will not be run again. For updating a task’s data (such as its schedule or priority), you will need to cancel the existing one and summon a new Meeseeks with the updated Task.
Meeseeks provides APIs to query a task’s current status at any time:
  • MeeseeksBox.getStatus(taskId) returns the TaskStatus of a single task, or null if that task does not exist.
  • MeeseeksBox.getAllTasks() returns a list of ScheduledTask objects, each containing the task’s MrMeeseeksId, TaskStatus, and other metadata (like run attempt count).
  • MeeseeksBox.watchStatus(taskId) provides a reactive stream of TaskStatus changes.
Tasks specify a TaskSchedule, which can be:
  • TaskSchedule.OneTime
  • TaskSchedule.Periodic
On Android, PeriodicWorkRequests are used under the hood. On iOS, BGAppRefreshTaskRequest and BGProcessingTaskRequest are used under the hood.
  • One-Time Task
  • Periodic Task
  val oneTimeSyncTask = Task(
    MeeseeksType.SYNC,
    schedule = TaskSchedule.OneTime(
      initialDelay = 0
    )
  )

  val meeseeksId = MeeseeksBox.value.summon(oneTimeSyncTask)
It depends on your TaskPreconditions. For instance, if requiresNetwork = true, the task will not be run unless a network is available.
  • Use exponential backoff (TaskRetryPolicy.ExponentialBackoff) for network-related tasks. This prevents tight loops and reduces server/battery load.
  • Limit max retries to avoid infinite attempts.
  • Use fixed intervals (TaskRetryPolicy.FixedInterval) if you’re sure the operation should repeat in consistent time blocks.
  • Log failed attempts thoroughly so you can identify repeated failures.
Because Meeseeks is a Kotlin Multiplatform library, you can implement a MeeseeksFactory that provides platform-specific code. For example, if you need iOS-specific APIs, you could use expect/actual classes or call Swift code from your iOS target. The library only orchestrates scheduling and persistence. What the MrMeeseeks does inside execute() is up to you.
Meeseeks uses TaskStatus to represent the lifecycle of tasks:
  • Pending: The task is scheduled but not currently running.
  • Running: The task is actively executing.
  • Finished: A terminal state indicating no further runs. It may be:
    • Completed (successful)
    • Cancelled (manually cancelled or platform removed it)
    • Failed (permanently failed)
When a task runs, if it returns:
  • TaskResult.Success: Meeseeks sets the task status to Finished.Completed.
  • TaskResult.Retry or TaskResult.Failure.Transient: Meeseeks reverts the task status to Pending so that it can be retried later (respecting any backoff or retry policy).
  • TaskResult.Failure.Permanent: Meeseeks sets the task status to Finished.Failed, meaning no more attempts will be scheduled.
Conceptually, you can visualize these transitions:
If a task is canceled via MeeseeksBox.sendBackToBox, it transitions to Finished.Cancelled. For tasks that fail with a transient error (e.g., network unavailable), the status simply goes back to Pending until the next run attempt.

Next steps

Yes siree! You’ve successfully initialized Meeseeks, configured your first tasks, and seen how to track results. Use the resources below to keep learning.