Closed cho-4 closed 1 month ago
Is there any reason to store data by sending it to the main isolate?
TaskHandler
runs in background isolate. This can continue to survive in the background with the foreground service.
but, Flutter app
runs in main isolate. It may be paused or detached in the background, so internal logic or services may not work properly.
What do you think about saving the value to the database in the onRepeatEvent callback, and then updating the screen by getting the value from the database when the Flutter app resumes?
Debugging mode does not pause the app. It just looks like it's working normally, but it's not.
To explain my current situation in more detail, mainscreen.dart is saving data to sleepdatabloc, a singleton object, approximately once every 10 seconds.
However, saving this directly to the database requires processing more resources than expected, so I tried to use the logic to save it to the database in onRepeatEvent().
However, taskhandler cannot access the singleton object I saved in mainscreen.dart, so the value cannot be saved in the database.
Yes. As I said last time, memory is not shared between the two isolates.
In conclusion, if you want to save data in the background, you should not save data in the Widget(mainscreen.dart).
Widgets can be destroyed at any time.
Can you provide sample project? What features do you want? I can help you.
@Dev-hwang Sorry for the late reply. Thank you for your kindness.
However, I don't think I can share the sample project due to personal reasons.
Instead, may I ask you a little more about this situation?
You said not to save data in the widget, so how should I save the data, for example?
I need to save the data handled by mainscreen.dart. So, is there a way to know the data in mainscreen.dart (widget)?
@cho-4
Please check just one thing.
Run this task and check whether notificationText is updated.
class MyTaskHandler extends TaskHandler {
int _count = 0;
@override
void onStart(DateTime timestamp) {
_updateNotificationText();
}
@override
void onRepeatEvent(DateTime timestamp) {
_count++;
_updateNotificationText();
}
@override
void onDestroy(DateTime timestamp) {
//
}
void _updateNotificationText() {
FlutterForegroundTask.updateService(notificationText: 'count: $_count');
}
}
Please tell me if notificationText is not updated.
@cho-4
The following functions manage synchronized data between TaskHandler and main isolate.
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();
}
You can get data when the flutter app is resumed
class MyTaskHandler extends TaskHandler {
int _count = 0;
@override
void onStart(DateTime timestamp) {
//
}
@override
void onRepeatEvent(DateTime timestamp) async {
_count++;
FlutterForegroundTask.saveData(key: 'count', value: _count);
// for foreground state
if (await FlutterForegroundTask.isAppOnForeground) {
FlutterForegroundTask.sendDataToMain(_count);
}
}
@override
void onDestroy(DateTime timestamp) {
//
}
}
class MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
void _onReceiveTaskData(Object data) {
sleepDataBloc.saveSleepBackupData(data);
}
void _onAppResumed() async {
final int? data = await FlutterForegroundTask.getData<int>(key: 'count');
if (date != null) {
sleepDataBloc.saveSleepBackupData(data);
}
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// Add a callback to receive data sent from the TaskHandler.
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// for background state
if (state == AppLifecycleState.resumed) {
_onAppResumed();
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
// Remove a callback to receive data sent from the TaskHandler.
FlutterForegroundTask.removeTaskDataCallback(_onReceiveTaskData);
super.dispose();
}
}
@Dev-hwang I ran the code you showed, but the norificationText is still not updated in release mode. But it works fine in debug mode.
I tested it with the example app for about a day and it worked normally.
tested env
Flutter 3.24.2 / Dart 3.5.2
Flutter 3.10.0 / Dart 3.0.0
tested device
- Galaxy Note 10 (Android 12)
- Galaxy Fold 4 (Android 14)
- Galaxy Fold 5 (Android 14)
https://drive.google.com/file/d/1G7RRp5X6MLIXQww3HRCp0nRlEBtcgTsH/view?usp=sharing
The service does not seem to be running in the background due to a specific manufacturer's problem or an unknown problem. Similar issues are being reported in other plugins.
If the app I provided runs normally, this may not be the above problem.
It will be difficult to solve the issue without detailed simulation and sample project to reproduce the problem.
@Dev-hwang I also use the latest versions of both Flutter and Dart, and my actual device is Galaxy S21 and uses Android 14.
In case it's a device problem, the other device, Galaxy A235 Android 14, still doesn't work in release mode.
@cho-4
Did you add pragma annotation?
@pragma('vm:entry-point')
void startCallback() {
// The setTaskHandler function must be called to handle the task in the background.
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
}
@Dev-hwang
Yes I added it
`@pragma('vm:entry-point') void startCallback() { FlutterForegroundTask.setTaskHandler(MyTaskHandler()); }
void main() { widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); FlutterForegroundTask.initCommunicationPort(); runApp(const MyApp()); }`
@cho-4
Please check whether notificationText is updated with the app I provided.
https://drive.google.com/file/d/1G7RRp5X6MLIXQww3HRCp0nRlEBtcgTsH/view?usp=sharing
this example, noficiationText is updated every 6 minutes.
You can also try the following:
goto android/app/proguard-rules.pro
add keep class flutter_foreground_task
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.embedding.**
-keep class com.pravera.flutter_foreground_task.** { *; }
@cho-4
Any news on the results?
@Dev-hwang I'm sorry for the late reply due to personal circumstances.
I think I didn't use taskhandler in the right place. It worked fine because I used it in the right place.
Thank you for your kindness in the meantime
ok. if you have any other issues or questions, create new issue at any time.
I'm the one who said last time that onRepeatEvent() wasn't working properly.
I am actually testing on a device using Android 14 OS. When I connect the device to a PC and check the log, it works properly in debug mode, but when I disconnect from the PC and check the function, it does not work properly whether in debug mode or release mode.
in main.dart `void startCallback() { FlutterForegroundTask.setTaskHandler(MyTaskHandler()); }
void main() { widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); FlutterForegroundTask.initCommunicationPort(); runApp(const MyApp()); } class MyTaskHandler extends TaskHandler {
@override void onStart(DateTime timestamp) { } @override void onRepeatEvent(DateTime timestamp) { FlutterForegroundTask.sendDataToMain(359); } @override void onDestroy(DateTime timestamp) { print('foreground service end'); } void onNotificationPressed() { FlutterForegroundTask.launchApp('/'); print('onNotificationPressed'); } }
void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { _requestPermissions(); _initService(); }); }
Future _initService() async {
FlutterForegroundTask.init(
androidNotificationOptions: AndroidNotificationOptions(
channelId: 'foreground_service',
channelName: 'Foreground Service Notification',
channelDescription:
'This notification appears when the foreground service is running.',
channelImportance: NotificationChannelImportance.LOW,
priority: NotificationPriority.LOW,
),
iosNotificationOptions: const IOSNotificationOptions(
showNotification: false,
playSound: false,
),
foregroundTaskOptions: ForegroundTaskOptions(
eventAction: ForegroundTaskEventAction.repeat(360000),
autoRunOnBoot: true,
autoRunOnMyPackageReplaced: true,
allowWakeLock: true,
allowWifiLock: false,
),
);
}
`
in mainscreen.dart `void _onReceiveTaskData(dynamic data) { sleepDataBloc.saveSleepBackupData(data); }
void initState() { super.initState(); FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData); ` According to the code, the taskhandler sends data 359 to mainUI once every 6 minutes and stores the value. This works fine when connected to a PC, but it cannot be run on the device alone. The version is 8.8.0, the most recent version, and the battery limit setting is also unlocked.