Skip to content

feat(android): work chaining via beginUniqueWork - #718

Draft
ened wants to merge 3 commits into
mainfrom
feat/work-chaining
Draft

feat(android): work chaining via beginUniqueWork#718
ened wants to merge 3 commits into
mainfrom
feat/work-chaining

Conversation

@ened

@ened ened commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements audit gap #4: WorkManager sequential work chaining (beginUniqueWork(...).then(...).enqueue()) for Android.

API shape

Workmanager().beginUniqueWork(
  'chain-name',
  existingWorkPolicy: ExistingWorkPolicy.keep,
  tasks: [
    WorkChainTask(taskName: 'step1', inputData: {'url': url}),
    WorkChainTask(taskName: 'step2', inputData: {'url': url}),
  ],
);

Why this shape (and the refinements vs. the raw proposal):

  • WorkChainTask is the unit of chaining. It mirrors the per-task configuration of registerOneOffTask minus the unique name (inputData, initialDelay, constraints, backoffPolicy/backoffPolicyDelay, tag, outOfQuotaPolicy, foregroundServiceConfig), so each chain step is a fully-specified one-off task. This is a faithful mapping: WorkManager chains are built from OneTimeWorkRequests.
  • A linear tasks list, not a fluent builder. beginUniqueWork(name, policy, first).then(...).enqueue() is exactly what the list maps to — first element starts the chain, the rest are then()-appended. A builder would add API surface without extra expressiveness, since WorkManager's branching (beginWith/combine) is intentionally out of scope.
  • One unique name per chain — same existingWorkPolicy semantics as registerOneOffTask (including the existing append → APPEND_OR_REPLACE mapping, kept for consistency).
  • Validation: empty tasks throws ArgumentError on the Dart side (and IllegalArgumentException as a native backstop).

Ordering & failure semantics

  • Strict ordering: step N+1 starts only after step N finishes with Result.success().
  • Permanent failure stops the chain: a step whose handler throws is reported as Result.failure(), and WorkManager stops the chain — remaining steps never run. (Verified natively: dependents are failed/cancelled by WorkManager and their runAttemptCount stays 0.)
  • false is a retry, not a failure: consistent with one-off tasks, returning false maps to Result.retry() — the chain holds and retries that step with its backoff policy before moving on. This is documented.

Android-only statement

Chaining is WorkManager-specific. The API exists on every platform (so code compiles everywhere) but:

  • iOS/macOS: WorkmanagerApple.beginUniqueWork throws UnsupportedError('Work chaining is not supported on iOS/macOS') — the same fail-loud pattern as cancelByTag/isScheduledByUniqueName on iOS.
  • Web: WorkmanagerWeb.beginUniqueWork throws UnsupportedError.
  • Chosen over a silent no-op because silently dropping a scheduling request is dangerous; documented in docs/customization.mdx and the capability matrix.

Compatibility

All new API — nothing existing changes. No signatures altered, no behavior changes to one-off/periodic registration. The one-off request builder was extracted into a shared internal createOneTimeWorkRequest (identical behavior, verified by the existing test suite). New Pigeon messages are additive; work-runtime 2.11.2 confirmed to have beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest), WorkContinuation.then, enqueue.

Tests

  • Dart: facade validation (empty chain), platform plumbing (recording fake platform), Android pigeon mapping via mocked channel (task list, policy, per-step backoff/constraints/foreground-service defaults), UnsupportedError on Apple/Web.
  • Kotlin (Robolectric, new WorkChainingTest, following ConstraintsMappingTest patterns): chain enqueued with correct prerequisite order, task-name/input-data payload convention, successful chain completes in sequence, a permanently failed step stops the chain (dependents never run), ExistingWorkPolicy.REPLACE applies to the chain, per-step backoff applied. Uses androidx.work:work-testing's synchronous driver (test-only dependency).

Docs & example

  • docs/customization.mdx: new "Work Chaining (Android only)" section (what it's for, ordering guarantee, failure semantics incl. false→retry, existing-work-policy note, conditional-chain guidance).
  • docs/index.mdx: capability-matrix row (Android ✅, others ❌/UnsupportedError).
  • Example app: "step1 → step2" chain (ordering visible via timestamps) and a chain with a failing step (final step never runs).

Checks

  • melos bootstrap ✅ · dart analyze clean ✅ · flutter test all packages ✅ · ./gradlew :workmanager_android:test (37 tests) ✅ · dart format clean ✅ · ktlint clean ✅ · flutter build apk --debug

@docs-page

docs-page Bot commented Aug 3, 2026

Copy link
Copy Markdown

To preview the documentation for this pull request, visit the following URL:

docs.page/fluttercommunity/flutter_workmanager~718

Documentation is deployed and generated using docs.page

ened added 2 commits August 3, 2026 17:47
- WorkManagerUtils: keep both the one-off builder (main) and the chain
  builder; chain builder drops the implicit setExpedited (aligned with
  0.10.2 explicit-only expedited semantics)
- WorkmanagerPlugin: both getWorkInfo and beginUniqueWork imports
- tests: rebuild the conflict-spliced test files from both source
  commits (expedited + work-info groups from main, chaining group from
  the original branch); apple tests get the beginUniqueWork
  UnsupportedError test inserted into main's file
- pigeon regenerated from the merged source
- WorkManagerUtils: keep both the one-off builder (main) and the chain
  builder; chain builder drops the implicit setExpedited (aligned with
  0.10.2 explicit-only expedited semantics)
- WorkmanagerPlugin: both getWorkInfo and beginUniqueWork imports
- tests: rebuild the conflict-spliced test files from both source
  commits; apple tests get the beginUniqueWork UnsupportedError test
- pigeon regenerated from the merged source
@ened
ened force-pushed the feat/work-chaining branch from cb3fcf5 to 2c490f2 Compare August 3, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant