Ensuring Completion of Operations During app Termination in Flutter - flutter

Let's say we have a Flutter app where we want to save some persistent data using shared preferences.
(We don't want to save the data persistently every time the user changes it because the UI depends directly on the data, and in order to save it we need to use await async, and that lags the UI), so we use WidgetsBindingObserver to detect when the app goes to the background in order to use that event as an efficient catch-all opportunity to save the data.
So, we have some code like this in the root page of our app:
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
// went to Background
myPersistentData.instance.write(); // <-------------- this is an async operation
}
if (state == AppLifecycleState.resumed) {
// came back to Foreground
}
}
This seems to work, but I'm concerned that the lifecycle could progress beyond "paused" (pause>stop>finish) so quickly that this "trailing" operation doesn't have time to finish... and thus the data would not be saved.
Is this a legitimate concern?

I would say as much as possible try to avoid calling async operations in the dispose or lifecycle methods of widgets, while you may be concerned that it may/may not complete i would recommend like the dart docs states here: https://api.dart.dev/stable/2.14.1/dart-async/unawaited.html, "Not all futures need to be awaited.", "You should use unawaited to convey the intention of deliberately not waiting for the future.". But generally, i'd avoid calling async functions in these methods.

It should be a concern for sure because the time available for an app to run in background is limited and not defined.
Based on the memory resources needed for the most important app, which is the one in foreground, the system may kill your app faster or at a later point in time. In either scenarios you don't have much time to complete the tasks, this event should be used to do fast clean up like killing tasks that are started, clearing token or maybe saving a token or a session info.
There are also scenarios which will cause problems for this approach, like the pone runs out of battery or the OS crashes, in this case you don't have any notification of app going into bg. Or the user force quits your app.
In your case, I would say you have another concern, saving data in shared preferences shouldn't affect your UI only if:
you save it synchronously which is a no no
you save a huge chunk of data in shared prefs, in this case you should use a database.
and a less likely scenario, you have a race condition while you save in shared prefs which are not thread safe.
I would suggest to rethink a bit your approach on this matter, maybe you can spawn async operations that save the data in certain points of the app (user exits a screen or whatever) while also keeping a copy in memory. Maybe is better to save it using a database. In the end is your decision, but my advice would be, don't rely on the app running in bg time.

For something like this, there is no way you can continue a service purely in Flutter. That's bummer of course but there are work-arounds you can use to do that.
If you want to approach this problem in a more convenient but difficult way, you can always create a service in native (Kotlin/Java in case of Android or Swift/Objective-C for iOS) and call your native functions using MethodChannels. Your native service can be alive even if you kill your app. Think of Push-notifications service where even if you kill your app, Push service is always alive to serve your notification.

Related

Flutter: Will callback supplied to Timer get fired when application goes to background

In Flutter if I create a Timer, will the supplied callback get executed if the application is in background?
I think documentation is not clear on this or I might have missed it (please supply a link if you find it anywhere).
I just tried doing this with a timer set up to 10 seconds and it works fine.
I assume that is approach is not very reliable and other methods* should be used instead. I think if the app is paused/terminated by operating system to preserve battery or due to low memory, nothing will be executed. But for me this situation could be an ok approach.
* I know there are isolates so I guess I could spawn the timer inside one of these. Downside is that the isolate must be regular function (or static method) so no access to application data in my scenario. Then there are different scheduling packages etc but I'm trying to avoid these for now. I know about background tasks but I'm really looking for an answer on code execution using timers.

What are the best practices when working with data from multiple sources in Flutter/Bloc?

The Bloc manual describes the example of a simple Todos app. It works as an example, but I get stuck when trying to make it into a more realistic app. Clearly, a more realistic Todos app needs to keep working when the user temporarily loses network connection, and also needs to occasionally check the server for updates that the user might have added from another device.
So as a basic data model I have:
dataFromServer, which is refreshed every five minutes, and
localData, that describes what changes have been made locally but haven't been synchronized to the server yet.
My current idea is to have three kinds of events:
on<GetTodosFromServer>() which runs every few minutes to check the server for updates and only changes the dataFromServer,
on<TodoAdded>() (and its friends TodoDeleted, TodoChecked, and so on) which get triggered when the user changes the data, and only change the localData, and
on<SyncTodoToServer>() which runs whenever the user changes the todo list, or when network connectivity is restored, and tries to send the changes to the server, retrieves the new value from the server, and then sets the new dataFromServer and localData.
So obviously there's a lot of interaction between these three methods. When a new todo is added after the synchronization to the server starts, but before synchronization is finished, it needs to stay in the local changes object. When GetTodosFromServer and SyncTodoToServer both return server data, they need to find out who has the latest data and keep that. And so on.
Coming from a Redux background, I'm used to having two reducers (one for local data, one for server data) that would only respond to simple actions. E.g. an action { "type": "TodoSuccessfullySyncedToServer", uploadedData: [...], serverResponse: [...] } would be straightforward to parse for both the localData and the dataFromServer reducer. The reducer doesn't contain any of the business logic, it receives actions one by one and all you need to think about inside the reducer is the state before the action, the action itself, and the state after the action. Anything you rely on to handle the action will be in the action itself, not in the context. So different pieces of code that generate those actions can just fire these actions without thinking, knowing that the reducer will handle them correctly.
Bloc on the other hand seems to mix business logic and updating the state. API calls are made within the event handlers, which will emit a value possibly many seconds later. So every time you return from an asynchronous call in an event handler, you need to think about how the state might have changed while that call was happening and the consequences this has on what you're currently doing. Also, an object in the state can be updated by different events that need to coordinate among themselves how to avoid conflicts while doing so.
Is there a best practice on how to avoid the complexity that brings? Is it best practice to split large events into "StartSyncToServer" and "SuccessfullySyncedToServer" events where the second behaves a lot like a Redux reducer? I don't see any of that in the examples, so is there another way this complexity is typically avoided in Bloc? Or is Bloc entirely unopinionated on these things?
I'm not looking for personal opinions here, only if there's something I missed in the Bloc manual (or other authoritative source) about how this was intended to work.

Android Async Task does not update UI when application is in background

The application I am working on downloads and parses a large xml file to populate UI elements, namely search and a spinner. This is done through an async task. If the user decides to switch out of the application during this, the information is downloaded correctly, but then when the application is resumed, the UI will not be updated.
Is this because the changes can't be made while the application is not active? What is the best way to go about checking whether the UI was not updated on resume? Or instead should I be doing something with the Async task, checking whether the UI thread is available? I'm having a hard time debugging this because it involves leaving the application which ends the debugger.
You can achieve this scenario through the broadcast receive.
Follow the step:
Solution 1:
Step 1;
Register the broadcast receiver before executing the Asyntask.
Step 2:
send Broadcast in onPostExecute method of Asyntask.
step 3:
And then you can able receive your broadcast message in your activity.
and do whatever you want.
Solution 2:
Otherwise you can use Interface Call back for this Scenario.
Hope It will help you.
It should'nt. App being in background means View objects may or may not be there. Actually the entire process may be stopped or just deleted by android.
Use a Service to truly do processing in background. When processing is complete but UI is not there, post a notification to let user know, OR, save the results and provide it to UI the next time it binds to your service and ask for same thing (a.k.a caching).
The application in background may not be live. The O.S may destroy it in case of memory constrains.
The trick is to try an alternate logic.
When the application moves from background to foreground onresume() is called ,you could try saving the data to db and update the content on the resume call.
FYI.onPause() and OnResume() is called when the application goes background and foreground respectively.

Can Android 'kill' an Activity without killing the application?

As we know, the default flow in Android for such scenario is calling the activity's respective onSaveInstanceState, onStop, onDestroy methods before releasing the reference to the Activity object.
However it appears I have a case when my application is on the background, the activity gets killed without those methods being called, but my application itself does not get destroyed.
However I am unable to force-reproduce this. Whenever I use applications on the foreground that require a lot of resources, the whole process gets killed, not just the activity.
Which kind of makes me wonder, because I believe the 'app killing' on low resources is essentially just the old signal way, does the Android system actually 'kill' (release) an activity instantly without calling these methods? Or am I chasing ghosts?
Android app out of memory issues - tried everything and still at a loss
This is not how things work. The only memory management that impacts activity lifecycle is the global memory across all processes, as Android decides that it is running low on memory and so need to kill background processes to get some back
Now the explanation in the official documents is more clear
The system never kills an activity directly to free up memory. Instead, it kills the process in which the activity runs, destroying not only the activity but everything else running in the process, as well. To learn how to preserve and restore your activity's UI state when system-initiated process death occurs, see Saving and restoring activity state.
It is possible Android OS kills only some of your activities even if your app is in foreground. For instance if you have two activities A and B, and when A calls startActivity / startActivityForResult to start activity B then Android may deceide to destroy activity A's instance because it is taking up too much memory space.
You can force killing activities which don't run in the foreground by checking Don't keep activities in developer options menu.
Does the Android system actually 'kill' (release) an activity
instantly without calling these methods?
Yes it does. Here's what the docs say in regards to onStop():
Note that this method may never be called, in low memory situations
where the system does not have enough memory to keep your activity's
process running after its onPause() method is called.
and in regards to onDestroy():
There are situations where the system will simply kill the activity's hosting
process without calling this method (or any others) in it, so it
should not be used to do things that are intended to remain around
after the process goes away.
Do not count on this method being called as a place for
saving data! For example, if an activity is editing data in a content
provider, those edits should be committed in either onPause() or
onSaveInstanceState(Bundle), not here.
"However I am unable to force-reproduce this." - you could reproduce this situation by sending your application in background and then using the DDMS to kill the process manually.

Data driven view iOS app

I am new to objective-c/cocoa programming. I am making an application which is to constantly sync with a server and keep its view updated.
Now in a nutshell, heres what I thought of: Initiate an NSTimer to trigger every second or two, contact the server, if there is a change, update the view. Is this a good way of doing it?
I have read elsewhere that you can have a thread running in the background which monitors the changes and updates the view. I never worked with threads before and I know they can be quite troublesome and you need a good amount of experience with memory management to get most out of them.
I have one month to get this application done. What do you guys recommend? Just use an NSTimer and do it the way I though of...or learn multithreading and get it done that way (but keep in mind my time frame).
Thanks!
I think using separate thread in this case would be too much. You need to use threads when there is some task that runs for considerable amount of time and can freeze your app for some time.
In your case do this:
Create timer and call some method (say update) every N seconds.
in update send asynchronous request to server and check for any changes.
download data using NSURLConnection delegate and parse. Note: if there is probability that you can receive a huge amount of data from server and its processing can take much time (for example parsing of 2Mb of XML data) then you do need to perform that is a separate thread.
update all listeners (appropriate view controllers for example) with processed data.
continue polling using timer.
Think about requirements. The most relevant questions, IMO, are :
does your application have to get new data while running in background?
does your application need to be responsive, that is, not sluggish when it's fetching new data?
I guess the answer to the first question is probably no. If you are updating a view depending on the data, it's only required to fetch the data when the view is visible. You cannot guarantee always fetching data in background anyway, because iOS can always just kill your application. Anyway, in your application's perspective, multithreading is not relevant to this question. Because either you are updating only in foreground or also in background, your application need no more than one thread.
Multithreading is relevant rather to the second question. If your application has to remain responsive while fetching data, then you will have to run your fetching code on a detached thread. What's more important here is, the update on the user interface (like views) must happen on the main thread again.
Learning multithreading in general is something indeed, but iOS SDK provides a lot of help. Learning how to use operation queue (I guess that's the easiest to learn, but not necessarily the easiest to use) wouldn't take many days. In a month period, you can definitely finish the job.
Again, however, think clearly why you would need multithreading.