-
Notifications
You must be signed in to change notification settings - Fork 28.4k
Instance state not saved when app is killed by OS #6827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
#3427 is also likely related. |
@eseidelGoogle That's right. In which format could the flutter Activity state be saved, if it can be at all as of the current version? |
Right now we don't do anything to save anything. The framework itself has very little state worth saving -- it's all animation and stuff like that -- so it may be that we always leave this up to the app to do. We should probably expose it at the Dart level though. (Right now it's only exposed at the Java level.) |
@Hixie On Android, all framework Views have their state automatically saved and restored by the system, which only requires the developer to save manually the non UI part of the instance state. Shouldn't flutter work the same way for all UI widgets to prevent developers from having to write all the boilerplate each time to save where the user was, the scroll position, the enabled state of some button, the state of the previous screen and so on…? |
In Flutter, there's very little framework state to save. For example, the enabled state of a button is not state, it's input provided by the application. The current route history is stored in the framework, but not stored in a state that the framework can rebuild (since it's all instances of objects provided by the application code). The scroll position is about the only thing we could actually store (and we do save that in a store currently, just not one that survives the app). But in any case we should definitely do better than today. |
As an experienced Android Developer, I'd like to take part to the conversation with iOS developers too when it'll be discussed so flutter can be the perfect framework to build iOS and Android apps. |
Whoa I didn't realise this was the case? This is actually critical, and it's worth mentioning that on devices with less memory the process could be killed quite easily! What about saving the |
@Takhion Could you link "PageStore" you're mentioning? I'm interested in trying to workaround this issue since it seems it won't be fixed any soon (31st december of 2029 is a liiittle far IMHO) |
@LouisCAD sure it's here: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/page_storage.dart I think you'll need to save more than that though: at least the current route(s) and maybe some state? |
We don't currently do anything to make this easy. We haven't studied this problem in detail yet. For now I recommend storing the information you want to persist manually, and applying it afresh when the app is restored. |
Do we expose the necessary lifecycle events ("you're about to be killed!", "congrats, you're now restored") into Dart code, so developers can handle persisting state? That seems like the sufficient capabilities to unlock devs to explore solutions. Thoughts? |
@sethladd Just exposing it to developers is not enough IMHO. Flutter needs to save UI state too, without developers having to do it manually repeatedly for each app with dirty and hardly maintainable verbose boilerplate code. |
@LouisCAD thanks for the feedback. I'm thinking in steps... what's step one? Do we have the lifecycle events exposed yet? |
@Hixie you mentioned over in #3427 that we do have hooks for lifecycle. Did you mean https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html ? |
Is Flutter using multiple Activities by default? (e.g. if you go to a new screen). Process death is fairly common, the user hits the Home button and launches some other memory/CPU intensive application and your application will be killed in the background (or your application has been in the background for too long). It's an integral part of Android OS - every screen (Activity) should be able to persist and restore its' state and the process can be killed at any time after onStop(). Android will recreate the backstack of Activities if the user returns to a killed app, the top Activity is created first and then activities in the backstack are recreated on-demand if you go back in the backstack history. This can be a bigger issue in case Flutter is using multiple Activities (not sure if it does). This means that if you don't have state saving implemented then the system will recreate the Activities but everything else is lost (process was killed), thus leading to inconsistency and crashes. I wrote an article about process death on Android https://medium.com/inloop/android-process-kill-and-the-big-implications-for-your-app-1ecbed4921cb |
Also there is no (clean) way to prevent Android from killing your application once it's past the onStop() state (after clicking Home or if the app is just not the current foreground app). So somehow you have to deal with it. Default Android widgets are saving their state (e.g. entered text in EditText) automatically into the Bundle instance. Your Activity will notify you that it's necessary to store your state by calling onSaveInstanceState(Bundle). So maybe Flutter should be able to forward this onSaveInstanceState callback from the Activity to your "screens". You will get the Bundle with the saved state back in the onCreate(Bundle savedInstanceState) or onRestoreInstanceState(Bundle savedInstanceState) lifecycle callback in your activity. So to recap - Flutter could maybe forward the onSaveInstanceState() and onRestoreInstanceState() callbacks to the developer. Ideally you would also wrap the Android Bundle object into something that can be also used in Flutter. The next step would be that all Flutter widgets inside the screen would be also notified about these callbacks and use them to persist their current state. Good luck with that :-). Please take care and don't introduce too much of this Android state / lifecycle hell to Flutter. I am not sure how that works on iOS - but I think there is something similar but it's not "necessary" to use it (?). |
A lifecycle callback would be fine for me. I would just store/load the serialized redux state. |
Thanks for all the feedback! @Takhion also offered to help here. Perhaps an API design and a library is a good start? If that library works, we can look to integrate more formally. Also, the library will help identify what we need to do at the low-level engine (if anything). Basically: what's the concrete API proposal? |
@DanielNovak Flutter uses it's own "routing" system and by default it integrates with Android through a single View in a single Activity. You can have a hybrid Android/Flutter app and as such potentially multiple Activities and Flutter Views, but in that case you could easily save/restore instance state through Android directly.
@zoechi you probably don't want to do that because the saved instance state Bundle has to go through IPC with a hard limit of 1MB for your entire Android app state. Instance state, by definition, should only be the pieces of data that would allow you to recreate the same conditions of whatever in-progress activity the user is performing, so: text input, scroll position, current page, etc. Anything else should either be persisted on disk or derived from other state. |
Flutter exposes the Android onPause lifecycle event. The Android onDestroy() lifecycle event is not exposed. I guess the right approach would be to hook into the onPause() event for storing instance related state. |
@raju-bitter onPause() is not optimal, it's better to hook into onSaveInstanceState(). Here is the javadoc from onSaveInstanceState which mentions that onPause() is called more regularly than onSaveInstanceState (you would be triggering state saving more often than necessary or when it's not necessary at all):
|
That's not my issue. |
I am making progress on providing a solution for this. For an early preview of how an app will be able to restore instance state once this is done check out this PR: flutter/samples#433. Unfortunately, it will take a little more time until its all done. |
The design document with details about how to do state restoration in Flutter is available here: http://flutter.dev/go/state-restoration-design |
The PR with the general state restoration framework as outlined in the design above has been posted: #60375. I am still working on adding some more test coverage. |
The general state restoration framework (#60375) has been submitted. I am now working on integrating it in our widgets. Going to start with scrollables, text fields, and the Navigator. |
You can implement this yourself by storing the data you want restored inside of a sqlite database. That's a technique that is used when writing servers. That has the benefit of restoring an app even if the battery dies on the phone or there is a kernel panic or whatever. I recommend making an OO wrapper around your SQLite calls. Dart doesn't have something like SQLite.Net which does it automatically for you unfortunately. edit: It's also something we did in the early days of mobile development because memory was so limited and sqlite is a great way to swap things between memory and disk. |
I am closing this issue since the general state restoration framework (and the Android embedding) has landed. The remaining work is tracked in separate issues:
|
Well Done!!!!! Thanks flutter!!! |
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of |
What is instance state, and why it exists
On Android, an Activity can be killed at any time by the system. This happens usually when Android needs memory when your Activity is not in the foreground, or because of a non-handled configuration change, such as a locale change.
To avoid the user having to restart what he did from scratch when Android killed the Activity, the system calls
onSaveInstanceState(…)
when the Activity is paused, where the app is supposed to save it's data in aBundle
, and passes the saved bundle in bothonCreate(…)
andonRestoreInstanceState(…)
when the task is resumed if the activity has been killed by the system.The issue about it in flutter
In the flutter apps I tried (Flutter Gallery, and the base project with the FAB tap counter), if I open enough apps to make Android kill the flutter app's Activity, all the state is lost when I come back to the flutter activity (while not having remove the task from recents).
Steps to Reproduce
This will allow to simulate when Android kills Activities because it lacks memory and you're not in the foreground.
What's expected: The app is in the same state that where we left off, with untouched UI.
What happens: The activity is restarted from scratch, losing all the UI state, even really really long forms.
The text was updated successfully, but these errors were encountered: