Hello flutter/mobile developers,
I have a particular problem at hand and I need some advice. I need to create an app that will do a certain task at specific times of the day. The times will change daily, and it will get the updated times via a REST call at midnight (or at off peak hours). The task must at the scheduled time no matter what, even after device restart, etc. I have looked into this extensively and I'm coming away with the feeling that Flutter does not handle a scheduled task well, especially in iOS which can terminate an app for whatever reason.
The options I have looked into are:
workmanager (https://pub.dev/packages/workmanager/changelog): This seems the most promising, if not for the minimum frequency of 15 minutes, which is not enough for me. I would need to check every minute if it is time to do the task. Is there a way around this limitation?
Native solution. I have very little-to-no experience with native iOS and Android coding so I would not prefer this solution. But through some research I have seen some examples where a task can be scheduled through this approach.
Server side notification. This seems like a good solution on the surface, and I need advice on this. The calculation whether it is time to run the task will happen server-side, and a notification (through Firebase, for example) can be sent to the device, which will trigger the task to be run. Is this even feasible, will the device actually run code when it gets the notification?
So these are my options. Which doe you think is the most doable? If someone with more mobile experience than me can help me out I would appreciate it, maybe there is a better solution that the three I have proposed. Thank you.
The times will change daily, and it will get the updated times via a REST call at midnight (or at off peak hours).
This smells like bad architecture. Why?
You are DDOS'ing your update server at midnight (if you have many users in the same timezone). You can stagger your HTTP requests by a few seconds to fix this, but this is a band-aid, not a long term solution.
because you are inverting the dependency: It sounds like timing is the responsibility of the server (hence your clients call it to ask for the latest time). Your clients should not be the ones asking the server for updates, because now your client has 2 jobs: Schedule the schedule updates and also schedule the scheduled task for the user. Making clients ask the server is the wrong direction. You should send messages from the server to update the schedule on the device.
Even if you do poll the server for schedule updates, you should make the mobile app schedule notifications/ alarm at the time you want. Using silent notifications is a known workaround the limitations of background scheduling on iOS. On Android, WorkManager is not the right library, you should use AlarmManager.
Here is how another StackOverflow thought about using local notifications to solve the problem: They used flutter_local_notifications
Answering the bullet points
On Android, don't use WorkManager, use AlarmManager instead, or even better, schedule notifications. Having said there are some manufacturer specific quirks on Android, see the flutter_local_notifications docs:
Consequently, scheduled notifications may not work when the application is in the background on certain devices (e.g. by Xiaomi, Huawei). If you experience problems like this then this would be the reason why. As it's a restriction imposed by the OS, this is not something that can be resolved by the plugin.
Its not necessary to use Native code, notifications is a standard feature.
You can keep the polling architecture for now since its simpler (but dirtier). Even if you would so the server side architecture, I would send messages from server to client to only update the client side schedule, not to trigger work/ user alerts, otherwise you are making your application dependent on the internet when it doesn't sound like the internet is required (unlike social media notifications). This would lead to bad user experience for badly connected devices.
You can send Data Notifications through Firebase, and handle them with the onBackgroundMessage callback. See here: https://firebase.flutter.dev/docs/messaging/usage#background-messages
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
From the documentation (Android/Apple):
It must not be an anonymous function.
It must be a top-level function (e.g. not a class method which requires initialization).
Related
I'm trying to create an app that manages a BLE connection in the background (receives notifications, reconnects and subscribes to a characteristic when connection is lost and the phone is moved near the BLE device again).
Right now I have the app somewhat built - everything that I need works relatively well but only when the app is open. Although the code isn't up to standard as this is my first app and I don't have any previous experience with Java/Kotlin/Dart (this is also why I'd rather not touch anything outside of flutter).
I need the app to send me local push notifications when tabbed out of the app or when screen lock is on and as far as I understand one could do this with isolates. I've searched a lot but found one guide which is kind of hard to understand as a beginner and also uses Kotlin.
Can I get away with only dart code for background code execution (described above) and is it suitable for a beginner?
Also, let me know if you have any alternative solutions since all I need is to get a notification on my phone whenever it receives a notification from a BLE device (and also auto connecting). I've already tried this with kivy since I'm more familiar with python but with no luck.
Talking about BLE there's this plugin by the way: https://pub.dev/packages/flutter_reactive_ble
As for the isolates, they are not ideal for this even though they do run separately, they'll get killed eventually. There's also a little helper package that makes working with isolates very straightforward if you want to try it: https://pub.dev/packages/computer
And so actually for the task at hand (background execution) I would suggest researching/using this one: https://pub.dev/packages/background_fetch
I would not encourage you to use any timer-based solution that tries to wake up your phone every 15 minutes or so, since that uses unnecessary CPU time.
Now I'm not that much into Flutter, but at least on Android, the "correct" way to have an ongoing connection (auto-connect) is to have a Foreground Service in the process running, so the process does not get killed. Then subscribe to characteristic notifications as usual, and handle the values as they arrive, either in Java or Flutter (by using some Java <-> Dart bridge).
I have some critical data to be synced with the server every half an hour or so. I tried using WorkManager but turns out that it cannot run reliably even if I have REQUEST_IGNORE_BATTERY_OPTIMIZATIONS. So then I thought I should use AlarmManager, but they wrote in the docs:
This is a case where you might be tempted to use a repeating alarm. But if you own the server that is hosting your app's data, using Google Cloud Messaging (GCM) in conjunction with sync adapter is a better solution than AlarmManager.
So then I went to SyncAdapter docs and they mentioned with a star:
We recommended WorkManager as the recommended solution for most background processing use cases. Please reference the background processing guide to learn which solution works best for you.
I also read in quite a few blog posts that Sync Manager will be phased out or replaced in the near future.
So I am back again with WorkManager. Is it a good idea to use SyncAdapter in 2020?
If you need to have a sync reliably every 30 minutes, use Firebase Cloud Messaging (the evolution of GCM) and use WorkManager to enqueue a Worker that does the actual backup upload when the application on the device receives the message.
If your application is going to be always in the background you can expect that it will drift into having less and less execution time while in the background. Ignoring battery optimization helps in that case.
Here you have the information regarding the different power buckets.
Note This is for stock Android, some OEMs add additional battery optimization that further reduce background tasks. You can see a collection on issues on https://dontkillmyapp.com/
Currently I have written custom LiveData class, which adds snapshot listener to document reference while being observed, thus providing easy way to update UI. I want to continue listening to the same document after app closes, and show updates in notification.
What would be a good way to do that? I have little experience with services, etc. but if I understand corectly, I should use either WorkManager or foreground service. Is there a solution which would allow to use the same listener for UI and background?
Most answers to similar problems suggest using FCM + Cloud Functions to send updates, but for my purposes I would like to have ongoing notification and also I've experienced delays with cloud functions, so I'd like to avoid going this way.
The way you'd like it to work is simply not possible anymore these days.
When your app is backgrounded on Android, the operating system will reduce its resources usage over time. This means that you can't rely on Firestore's usual mechanism to receive updates from the server, not even in a background service.
The idiomatic way to deliver updates to an app that is not active in the foreground is (as you've found) to send FCM messages to that app from a server or Cloud Functions. If you're having trouble making this work, we'll be better able to help if you post the minimal, complete/standalone code with which we can reproduce where you got stuck.
i'm creating android app, and using Firebase.
I have 'Missions', and 'Mission' have status.
If the status changes to 'Active' - I need to 'Wake up' my app and run the code of 'ActiveMissionActivity'.
(The work of the service should be just 'Waking up the app' and go to another activity)
I could write missionsDbRed onChildChanged listener which will be activated when the status is active - but only when the app is running.
What is the good practice for this issue? My friend suggested me to use 'Android Service', but i'm not sure which type do I need, and if there is something that works well with the Firebase DB.
For example i've seen 'Firebase Cloud Functions' But i'm not sure which one is more suitable and why. Thanks
As push-notifications are not a solution to your issue, the best way of handling this matter is by building a service that checks (in the database) if the value you are interested in has changed.
Now regarding the service, a bad idea would be to run a service that continuously checks that value. This would result in a huge battery drain and you risk your process to be killed as android is freeing up resources. you can notice this here.
You can fix this by telling the service when to start checking the value you are interested in.(e.g. by using Alarm API and assuming you know when this should happen) You can see something similar here.
Keeping your service alive as less as possible is probably the safest way.
As for google cloud functions, you might wanna use those for sending notifications prior to the actions that happen through the service you established.
So if the status changes to active while your app is closed, so that means the value changed somewhere out of your app right? If that's the case simply send a push notification to users device to wake it up. It is a lot more performant than using a background service.
I have been developing for Android for little less then 2 years, and I am still puzzled by this seemingly simple question.
When should one implement a service?
From my experience there are some rare cases but I am questioning this because on every phone there are quite a lot of them running and I doubt it's just a poor application design.
This is essentially core of my question but following are some of my experiences and thoughts about the subject which can explain my question in more detail.
In all apps that I have developed only one really required a service. It was a background sound recorder and I was using it as Foreground service with notification since I wanted buttons to be able to control it (like music players do for example).
Except this I never really saw a requirement for the constantly running service because:
A) Intent listeners (Manifest registered BroadcastReceivers) are quite a useful feature and using them as you know is usually enough for many use-cases (for example showing notifications).
B) If scheduled execution is a must one can subscribe to alarm events.
C) I know that service in Android is quite different then for example in Windows since in Android services are just a "package" to organize your code in and have a the system manage the lifetime of the object. Services use the Main Thread but it's customary to spawn new threads in them.
D) In the development documentation services are suggested for network communication and background calculations but I don't get why you should not just use AsyncTasks for that. I am a big fan of these and use them extensively for lot of things from downloading data from the internet to doing FFT calculations under time critical conditions.
E) I get the usefulness of Foreground services but why are people using background services so much (excluding the system apps).
Those are my thoughts about the SERVICE and I hope someone with more experience will be able to explain these PROS and CONS (along with others that I probably missed).
When should one implement a service?
When you have work -- delivering value to the user -- that:
Needs some time to complete, perhaps longer than you have time for in the component wishing the work to be done, or
Is delivering that value under user control (e.g., music player, controlled by play/pause buttons in a UI), or
In rare cases, needs to be running continuously, as it delivers value continuously
there are quite a lot of them running and I doubt it's just a poor application design
Some are likely to be poor implementations, either due to technical misunderstandings, or other concerns (e.g., making marketing happy) trumping making users happy.
It was a background sound recorder and I was using it as Foreground service with notification since I wanted buttons to be able to control it (like music players do for example)
That is a reasonable use for a service, IMHO.
Intent listeners are quite a useful feature and using them as you know is usually enough for many use-cases (for example showing notifications)
I assume that by "Intent listeners" you mean manifest-registered BroadcastReceivers. In that case, if the work to be done by the BroadcastReceiver will take more than a millisecond, that work should be delegated to an IntentService for completion. onReceive() is called on the main application thread, and it is not safe for a manifest-registered BroadcastReceiver to fork a bare thread, as the process could go away shortly after onReceive() returns. However, in these cases, the service is usually short-lived (e.g., do some network I/O and disk I/O, then go away).
In the development documentation services are suggested for network communication and background calculations but I don't get why you should not just use AsyncTasks for that
An AsyncTask is a fine solution for background work that is:
Requested by the UI (activity or fragment), and
Will take less than a second or so, and
Is non-critical
For example, if you are downloading avatars to show in a ListView, AsyncTask is probably a fine choice, whether you use them directly or use some image-fetching library that uses them internally.
Conversely, if the user buys an MP3 through your app, and you need to download that MP3 file, an AsyncTask is not a good solution. That could easily take over a second. While the download is going on, the user could switch away from the app (e.g., press HOME). At that point, your process is eligible to be terminated... perhaps before your download is complete. Using an IntentService to manage the download is a signal to the OS that you are really doing work here, adding value to the user, and so the process will be left alone for a little while.
Note that if the background work might take 15+ seconds, WakefulBroadcastReceiver or my WakefulIntentService is probably a good idea, so the device does not fall asleep while you are trying to wrap up this bit of work.
I can name some of the Service uses from my experience:
to implement
location listener,
sound module, generating various voices
in app content updates,
API, provide services to other apps
in app billing
Communication with webservices (if requests frequency is high)
actually (excluding 5.) they all are working for the whole app duration, they are using some of the other android services, also they manage their state. I suppose one of the important thing here is state management during application life cycle changes.
I prefer to look at AsyncTasks in a same way as Executors (ExecutorService), they should be executed sequentially and for small tasks.
In the android website, you can find a table when to use Service, Thread, or WorkManager (the new API for scheduling jobs, currently in alpha as of this comment posted). https://developer.android.com/guide/background/#table-choose
The website also state that you need to use started service only as last resort. The Android platform may not support started services in the future. Refer to this link https://developer.android.com/topic/performance/scheduling#services
You should avoid using started services that run perpetually or perform periodic work, since they continue to use device resources even when they aren't performing useful tasks. Instead, you should use other solutions that this page describes, and that provide native lifecycle management. Use started services only as a last resort. The Android platform may not support started services in the future.
If you consider UI and bound services, u would think that both can exist and not be doing anything for certian periods. In such scenarios, your UI can be recreated a lot of times however service does not. And this is where service is important. Lets say you are processing images and then rotate device you want processing to continue while UI is being recreated. You recording a voice and then rotate device. These are one of the places where I find service very important. (Having lot of heavy data processing, interaction with web, that could be few seconds)