fluttercommunity / flutter_workmanager

A Flutter plugin which allows you to execute code in the background on Android and iOS.
858 stars 273 forks source link

🐞[Android][Handling cancellation of running work] #432

Open navaronbracke opened 2 years ago

navaronbracke commented 2 years ago

Version workmanager: 0.5.0

Describe the error

When a task is cancelled by WorkManager, there is no way to intercept the cancellation.

Steps to reproduce

1) Start a one off task with a network constraint

    Workmanager().registerOneOffTask(
      'taskUniqueName-${DateTime.now().millisecondsSinceEpoch}',
      'someTaskName',
      constraints: Constraints(networkType: NetworkType.connected),
      outOfQuotaPolicy: null,
    );

2) while the task is running, disconnect from the internet 3) the task is cancelled

I/WM-WorkerWrapper(12832): Work [ id=ab6c64e1-f244-44ff-8790-b2f79aafd52a, tags={ be.tramckrijte.workmanager.BackgroundWorker } ] was cancelled
I/WM-WorkerWrapper(12832): java.util.concurrent.CancellationException: Task was cancelled.
I/WM-WorkerWrapper(12832):      at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184)
I/WM-WorkerWrapper(12832):      at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514)
I/WM-WorkerWrapper(12832):      at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
I/WM-WorkerWrapper(12832):      at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:311)
I/WM-WorkerWrapper(12832):      at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
I/WM-WorkerWrapper(12832):      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
I/WM-WorkerWrapper(12832):      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
I/WM-WorkerWrapper(12832):      at java.lang.Thread.run(Thread.java:919)

Proposal

The plugin should expose a stream of cancellation events for the scheduled tasks. There could be an EventSink on the native side that gets its events from invoking

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/managing-work#onstopped_callback

in the worker implementation. The event should have enough information so that a developer can know which task was cancelled. Thus an event would at least need the unique name (and the original input data?)

Output of flutter doctor -v

[βœ“] Flutter (Channel stable, 3.3.2, on macOS 12.2.1 21D62 darwin-x64, locale
    en-BE)
    β€’ Flutter version 3.3.2 on channel stable at
      /Users/navaronbracke/Documents/flutter
    β€’ Upstream repository git@github.com:flutter/flutter.git
    β€’ Framework revision e3c29ec00c (3 weeks ago), 2022-09-14 08:46:55 -0500
    β€’ Engine revision a4ff2c53d8
    β€’ Dart version 2.18.1
    β€’ DevTools version 2.15.0

[βœ“] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    β€’ Android SDK at /Users/navaronbracke/Library/Android/sdk
    β€’ Platform android-33, build-tools 33.0.0
    β€’ ANDROID_HOME = /Users/navaronbracke/Library/Android/sdk
    β€’ Java binary at: /Applications/Android
      Studio.app/Contents/jre/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build
      11.0.12+0-b1504.28-7817840)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 13.4.1)
    β€’ Xcode at /Applications/Xcode.app/Contents/Developer
    β€’ Build 13F100
    β€’ CocoaPods version 1.11.2

[βœ“] Chrome - develop for the web
    β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 2021.2)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ Flutter plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
    β€’ Dart plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart
    β€’ Java version OpenJDK Runtime Environment (build
      11.0.12+0-b1504.28-7817840)

[βœ“] VS Code (version 1.71.2)
    β€’ VS Code at /Applications/Visual Studio Code.app/Contents
    β€’ Flutter extension version 3.48.0

[βœ“] Connected device (2 available)
    β€’ macOS (desktop) β€’ macos  β€’ darwin-x64     β€’ macOS 12.2.1 21D62 darwin-x64
    β€’ Chrome (web)    β€’ chrome β€’ web-javascript β€’ Google Chrome 105.0.5195.125

[βœ“] HTTP Host Availability
    β€’ All required HTTP hosts are available

β€’ No issues found!
ened commented 2 years ago

This is a great idea.

Why would you want to intercept - e.g. what would you like to do as a developer with the information? Reschedule another task?

navaronbracke commented 2 years ago

Well, I had a task indicator that would show progress (pending, running, done) and I was looking for a way to indicate a cancelled state.

ened commented 2 years ago

So your use case is just to show when things have failed. How would you handle the case when the task runs in the background but your main application is not visible?

navaronbracke commented 2 years ago

In my use case, I let the background task write the progress information to a database (which uses dart:ffi since it needs to be accessible across both the foreground Flutter Engine & the background Flutter Engine, which don't live in the same engine group). I don't show any updates when the app is in the background. When the application is in the foreground, I just observe the database for updates.