Closed Elvis5566 closed 3 weeks ago
Are you saying that the TaskHandler doesn't work when clear the app from recent apps list?
Yes! That's what I meant, and for android specifically.
Please update to the latest version and check.
It doesn't work.
Swipe the app from recent app list will NOT kill the app when the service isSticky, thus, won't trigger service restart.
This feature has nothing to do with REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
permission.
Version 6.1.2, the version I used, is already able to restart the app if the user ignore battery optimizations.
I've tried this commit and it works.
https://github.com/Dev-hwang/flutter_foreground_task/compare/master...Elvis5566:flutter_foreground_task:master
PS: my device is android 10.
@Elvis5566
that's weird.. Even if the Activity is destroyed, the TaskHandler will not be terminated while the service is alive.
When you swipe the app, is TaskHandler onDestroy called? or is it just dart code that stop working?
TaskHandler.onDestroy did not be called.
When I swiped the app, I got a log I/ViewRootImpl@ceadc95[MainActivity](26753): dispatchDetachedFromWindow
, and dart code stop working.
What I did is not restarting the service; it just executes dart code again.
@Elvis5566
Does the example stop the same way?
Yes, actually, I cannot use my app to test the latest version due to dependency issue, so I created a sample minimal app using the example code to test version 7.4.3. When I swipe the app, the dart code just stop working.
It can be verified quite easily. Run a timer to print log every second, then swipe the app, you can see the timer stop working.
@Elvis5566
(tested debug/profile mode)
Flutter 3.22.2 / Dart 3.4.3
Flutter 3.10.0 / Dart 3.0.0
Real Device
- Galaxy S10 (Android 10)
- Galaxy Note 10 (Android 12)
- Galaxy Fold 4 (Android 14)
- LG Q92 (Android 10)
Virtual Device (with Google Play)
- Android 14
- Android 12
- Android 10
it worked normally when tested with the above device.
i don't know if the Flutter version is affected or if the problem is with the device manufacturer...
what is your Flutter/Dart version and device manufacturer?
check if the same problem occurs on the virtual device.
interesting... My device is samsung note 9 / android 10 / flutter 3.16.9 / dart 3.2.6 I've tried simulator on android 14, still doesn't work. I don't have more real devices atm. https://github.com/Elvis5566/foreground_task_trial Here is the app I used for testing; Can you verify whether the timer keeps working on your devices when you swipe the app? It's so weird...
@Elvis5566
Flutter has main isolate
and background isolate
. The main isolate
is called the UI isolate
and is created when the app starts and destroyed when it exits. No reference variables or state are shared between two isolates and cannot be accessed.
MyTimer or static instance is created in main isolate. Therefore, when the app terminates, the static instance is removed.
TashHandler is created in background isolate. You can think of this as another process or another app.
If you want to share data between different isolates, you can use the method below:
sendPort
and sendData
This plugin supports two-way communication between TaskHandler and UI. The data trying to send must be a type provided by Flutter. like int, double, bool, String, Map. If you want to send a custom object, send it in String format using jsonEncode and jsonDecode. JSON and serialization >> https://docs.flutter.dev/data-and-backend/serialization/json
// send (TaskHandler -> UI)
@override
void onStart(DateTime timestamp, SendPort? sendPort) async {
sendPort?.send(Object); // this
}
// receive
void _onReceiveData(dynamic data) {
if (data is int) {
print('count: $data');
} else if (data is DateTime) {
print('timestamp: ${data.toString()}');
}
}
// send (UI -> TaskHandler)
void _sendData() {
final Random random = Random();
final int data = random.nextInt(100);
FlutterForegroundTask.sendData(data); // this
}
// receive
@override
void onReceiveData(Object data) {
print('onReceiveData: $data');
}
shared_preferences
There are some functions for storing and managing data that are only used in this plugin.
void function() async {
await FlutterForegroundTask.getData(key: String);
await FlutterForegroundTask.getAllData();
await FlutterForegroundTask.saveData(key: String, value: Object);
await FlutterForegroundTask.removeData(key: String);
await FlutterForegroundTask.clearAllData();
}
@Dev-hwang
Thanks for the explanation. I see what you mean, but technically, I am not trying to share data between isolates or trying to run tasks in a background isolation. I am building a tracking app. If I use this approach, I will have to move quite a lot business logic, such as listening location update, saving db model and tracking logic to background isolation, then using messages to communicate between UI and background isolation. I could, but this approach will introduce a lot of additional burden, which is just not practical for my circumstances. What I need is to ensure a piece of my code can be run in the background. So basically, My workaround to this problem is
PS: I hate this isolation mechanism 🤣
I think you should always put your task
on the foreground service whether the app is launched or in the background, and if you want to update the ui just use sendPort and sendData.
It's simpler this way.
@WingCH
If the 'Task' is just like a function, you run it in the background. Then, when it finishes, it returns the result via sendPort. That makes sense and indeed is simpler.
But if the Task
is like a service, which returns multiple data every second, such as location, duration, speed, etc..
And you have multiple command to control the service, ex: Start, Pause, Resume, Stop, etc...
In addition to the overhead of handling these communications, that is a lot of unnecessary data serialization/deserialization.
That's why I don't see it is simper.
When the user clears apps from recent apps, the MainActivity get destroyed, which causes the dart code stop working. We could set
android:stopWithTask="false"
to keep the foreground service running, then listenActivityAware.onDetachedFromActivity
and executeDartCallback again.Do you think it's a good idea to add this feature?