agentsclimarketplace

Background work optimization

Skill almasumdev/awesome-mobile-performance-agent-skills/.github/skills/battery/background-work-optimization

Agent skills for profiling and optimizing mobile app performance (startup, memory, frame-rate, network).

Install
npx -y skills add almasumdev/awesome-mobile-performance-agent-skills --skill background-work-optimization

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Schedule, batch, and constrain background work with WorkManager, BGTaskScheduler, and push-over-poll patterns to minimize battery and data cost.

SKILL.md

6.3 KB, as published. Nobody here has run it

Background Work Optimization

Instructions

Background work is where undisciplined apps burn battery and wake radios. Modern iOS and Android both expect you to declare constraints and let the OS pick the best moment to run. This skill covers how to convert ad-hoc background work into OS-friendly, batched, constraint-aware jobs.

1. Choose the Right Primitive

Android:

NeedUse
Deferred, guaranteed, constrainedWorkManager
Exact, wall-clock wake (rare)AlarmManager.setExactAndAllowWhileIdle (only with user-visible UI)
Ongoing service user expects (music)ForegroundService with proper foreground type
Data sync when idle + chargingWorkManager with requiresCharging(true)
Real-time updatesFCM / WebSocket kept alive by the OS, not polling

iOS:

NeedUse
Deferred data refreshBGAppRefreshTaskRequest
Longer ML / sync jobBGProcessingTaskRequest
"Wake me when event happens"Silent push (content-available: 1) + short handler
Background downloadURLSession.background(withIdentifier:)
Ongoing (audio, nav, VoIP)Background mode in Info.plist

2. WorkManager Patterns (Android)

Register a unique periodic job — de-dup across reinstalls:

val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED)
    .setRequiresBatteryNotLow(true)
    .setRequiresStorageNotLow(true)
    .build()

val req = PeriodicWorkRequestBuilder<SyncWorker>(6, TimeUnit.HOURS)
    .setConstraints(constraints)
    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.MINUTES)
    .build()

WorkManager.getInstance(ctx).enqueueUniquePeriodicWork(
    uniqueWorkName = "feed-sync",
    existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.KEEP,
    request = req,
)

Rules:

  • Never chain > 3 workers — chain explosions cause unclear failure modes.
  • Use CoroutineWorker for suspend functions; return Result.retry() on transient failures, Result.failure() on permanent ones.
  • Avoid ExpeditedWorkRequest for non-urgent work — it burns the app's foreground quota.

3. BGTaskScheduler Patterns (iOS)

Info.plistBGTaskSchedulerPermittedIdentifiers:

<array>
  <string>com.example.app.refresh</string>
  <string>com.example.app.processing</string>
</array>

Register and schedule:

func registerHandlers() {
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.example.app.refresh",
        using: nil
    ) { task in
        self.handle(task: task as! BGAppRefreshTask)
    }
}

func scheduleAppRefresh() {
    let req = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
    req.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)
    try? BGTaskScheduler.shared.submit(req)
}

func handle(task: BGAppRefreshTask) {
    let op = SyncOperation()
    task.expirationHandler = { op.cancel() }
    op.completionBlock = { task.setTaskCompleted(success: !op.isCancelled) }
    OperationQueue().addOperation(op)
    scheduleAppRefresh()   // re-schedule for next cycle
}

4. Replace Polling with Push

Polling eats battery; push costs almost nothing when the OS manages the socket. Replace:

// BAD — 30 s polling loop
setInterval(fetchMessages, 30_000);

With an FCM / APNs silent push that triggers a short fetch only when something changed. For RN, use @react-native-firebase/messaging; for native, the platform SDK.

5. Batching Requests

Group small requests to reduce radio wake costs. One request / 30 s is ~10× worse than ten requests once every 5 min.

Pattern: coalesce by debouncing "dirty" state and flushing in one network round-trip:

private val pending = MutableSharedFlow<Change>(extraBufferCapacity = 64)

init {
    pending
        .buffer(Channel.UNLIMITED)
        .debounce(500.milliseconds)
        .onEach { changes -> api.flushBatch(changes) }
        .launchIn(scope)
}

6. Respect Doze, App Standby, Low Power Mode

  • Android: assume the device is in Doze at night. Do not rely on alarms that fire during Doze.
  • iOS: Low Power Mode disables BGAppRefresh. Observe NSProcessInfo.isLowPowerModeEnabled; pause optional animations, video prefetch, high-res downloads.
NotificationCenter.default.addObserver(
    forName: .NSProcessInfoPowerStateDidChange, object: nil, queue: .main
) { _ in
    if ProcessInfo.processInfo.isLowPowerModeEnabled { reducePrefetch() }
}

7. Make Retries Cheap

Retries on failure must back off exponentially and respect Retry-After. A naïve retry storm after an outage is a frequent cause of "why did battery die after the server came back up?"

const delay = (attempt: number) => Math.min(2 ** attempt * 1000 + jitter(), 60_000);

8. Verify

  • Android: adb shell dumpsys jobscheduler shows scheduled, pending, and running jobs.
  • iOS: Debugger → Simulate Background Fetch in Xcode; log BGTaskScheduler.shared.pendingTaskRequests.
  • Dashboards: rate of wakeups per user per hour; foreground CPU time; radio-on time.

Checklist

  • WorkManager / BGTaskScheduler used for all deferred work.
  • All jobs have constraints (network type, battery, charging) when applicable.
  • Polling replaced with push where real-time is needed.
  • Request batching / debouncing in place for "dirty state" flushes.
  • Low Power Mode / Battery Saver observed and non-essential work paused.
  • Retries are exponential with jitter and Retry-After respected.
  • Background wakeup and CPU time telemetry in the dashboard.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.