Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upInstance state not saved when app is killed by OS #6827
Comments
eseidelGoogle
added
engine
framework
▣ platform-android
labels
Nov 12, 2016
This comment has been minimized.
This comment has been minimized.
|
#3427 is also likely related. |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Nov 12, 2016
|
@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? |
This comment has been minimized.
This comment has been minimized.
|
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.) |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Nov 12, 2016
|
@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…? |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Nov 25, 2016
|
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. |
eseidelGoogle
referenced this issue
Nov 28, 2016
Closed
If I switch apps while typing text, the text is lost #7033
Hixie
added this to the
4: Make shippers happy milestone
Feb 27, 2017
This comment has been minimized.
This comment has been minimized.
Takhion
commented
Sep 24, 2017
•
|
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 |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Sep 27, 2017
|
@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) |
This comment has been minimized.
This comment has been minimized.
Takhion
commented
Sep 27, 2017
|
@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? |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Oct 7, 2017
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@LouisCAD thanks for the feedback. I'm thinking in steps... what's step one? Do we have the lifecycle events exposed yet? |
This comment has been minimized.
This comment has been minimized.
Takhion
commented
Oct 7, 2017
This comment has been minimized.
This comment has been minimized.
|
@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 ? |
This comment has been minimized.
This comment has been minimized.
DanielNovak
commented
Oct 10, 2017
•
|
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 |
This comment has been minimized.
This comment has been minimized.
DanielNovak
commented
Oct 10, 2017
•
|
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 (?). |
This comment has been minimized.
This comment has been minimized.
|
A lifecycle callback would be fine for me. I would just store/load the serialized redux state. |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
Takhion
commented
Oct 11, 2017
•
@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. |
Hixie
referenced this issue
Oct 17, 2017
Closed
App state is lost when changing display density on Android #12588
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
DanielNovak
commented
Oct 18, 2017
|
@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):
|
This comment has been minimized.
This comment has been minimized.
That's not my issue. |
flutter
deleted a comment from
gitspeak
Dec 18, 2018
mehmetf
referenced this issue
Dec 18, 2018
Open
Clarification Discussion for https://github.com/flutter/flutter/issues/6827 #1
This comment has been minimized.
This comment has been minimized.
gitspeak
commented
Dec 18, 2018
|
@passsy Please consider my comments below as a sincere attempt to provide constructive feedback
Several issues:
Very much disagree, besides having the same issues surrounding "Solution A", it adds a new risk of opening a can of data duplication worms.
Having same issues surrounding "Solution A".
Disagree. Android's automatic retention of the Widget Tree in those cases where it's state is subject to being tossed is very helpful! Why on earth would you want to move this responsibility in it's entire to the app developer?? Why does this stand in contrast to UI-state separation ?? Actually, this automatic way of saving / restoring the Widget Tree seems like a perfect example of UI/State separation.. |
This comment has been minimized.
This comment has been minimized.
aletorrado
commented
Dec 19, 2018
|
The "Solution A" seems perfectly plausible to me when using appropiate locks, and no ANR should be triggered if the amount of work is reasonable. Also, by embracing the platform's notification mechanism it avoids doing unnecessary serialization work all the time, which may be expensive to do for every single keystroke. About the possibility to automate this behavior, it seems clear to me that having no reflection mechanism in the Dart runtime, and having no structured way to store the state in memory in Flutter, that's not possible by any means. At the moment, the simplest approach I can think of doing this would be to opt-in to keep state in a single dynamic object (just like |
This comment has been minimized.
This comment has been minimized.
gitspeak
commented
Dec 19, 2018
•
I agree. when ... Where the Android Model is far more simple and robust since it does not require the developer to deal with locks, it does not persist transient state to disk and it retains state only for those cases where it's relevant something you (flutter) can't do without hooking into the Android lifecycle events and the onSaveInstance facility - to begin with, so what is it good for?
There is no requirement to persist every key stroke but rather a snapshot consisting of selected states |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Dec 19, 2018
|
The data should just be stored in the Bundle as a ByteArray to avoid
triggering any ANR or lag caused by I/O on main thread.
…On Wed, Dec 19, 2018, 6:08 PM Alejandro Torrado ***@***.***> wrote:
The "Solution A" seems perfectly plausible to me when using appropiate
locks, and no ANR should be triggered if the amount of work is reasonable.
Also, by embracing the platform's notification mechanism it avoids doing
unnecessary serialization work all the time, which may be expensive to do
for every single keystroke.
About the possibility to automate this behavior, it seems clear to me that
having no reflection mechanism in the Dart runtime, and having no
structured way to store the state in memory in Flutter, that's not possible
by any means. At the moment, the simplest approach I can think of doing
this would be to opt-in to keep state in a single dynamic object (just like
this.props and this.state on React).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6827 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGpvBUQk17YOsjLMHTWcdJHL9rR71u6lks5u6nKGgaJpZM4KwSuX>
.
|
This comment has been minimized.
This comment has been minimized.
Zhuinden
commented
Dec 19, 2018
|
The real trickery is building up this ByteArray you speak of |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Dec 20, 2018
|
I'd rather say that the real trickery is to be able to restore this
ByteArray properly.
…On Wed, Dec 19, 2018, 6:41 PM Gabor Varadi ***@***.***> wrote:
The real trickery is building up this ByteArray you speak of
|
This comment has been minimized.
This comment has been minimized.
Zhuinden
commented
Dec 20, 2018
|
No. Once you have the byte array, it is very easy. Just show a loading indicator while you're loading it (assuming you've told yourself with channels that you need to load it). |
This comment has been minimized.
This comment has been minimized.
LouisCAD
commented
Dec 20, 2018
|
The real issue is Flutter integration, what I call the restore part.
Navigation should be saved automatically, and user input too, just like on
native Android apps.
Because of this issue, Flutter apps can't integrate correctly in Android as
they don't behave correctly when there's RAM pressure and the user switches
apps in these conditions before coming back.
…On Thu, Dec 20, 2018, 2:42 AM Gabor Varadi ***@***.***> wrote:
No. Once you have the byte array, it is very easy. Just show a loading
indicator while you're loading it (assuming you've told yourself with
channels that you need to load it).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6827 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGpvBR4rmSxelodAjZvcQ6kOwPd7yxKPks5u6ushgaJpZM4KwSuX>
.
|
This comment has been minimized.
This comment has been minimized.
|
A bunch of us discussed this a bit more here: mehmetf#1. Relevant points to this issue:
|
This comment has been minimized.
This comment has been minimized.
pepegich
commented
Dec 27, 2018
can you provide an example this code? |
This comment has been minimized.
This comment has been minimized.
I can't provide a copy/paste type of example but here's what's happening: Application
Activity
@Override
public FlutterNativeView createFlutterNativeView() {
FlutterNativeViewProvider provider = (FlutterNativeViewProvider) getApplication();
return provider.getFlutterNativeView();
}
@Override
public boolean retainFlutterNativeView() {
return true;
}Something along these lines should get you going. Note that your Flutter application will start running before an Activity (and thus a window) is available. Therefore you should not be calling Disclaimer(s)
|
This comment has been minimized.
This comment has been minimized.
jsroest
commented
Dec 27, 2018
To be sure that you have a valid state-file you can write to a .tmp file and then rename when writing is done. If you also keep the previous state file, you always will end up with valid state. Also when the process is killed while saving the state. |
This comment has been minimized.
This comment has been minimized.
gitspeak
commented
Dec 27, 2018
But not necessarily the correct state, which is kind of the whole point.. There is also no requirement for persisting state to non-volatile memory to begin with .. |
This comment has been minimized.
This comment has been minimized.
jsroest
commented
Dec 28, 2018
In many situations having a consistent state is more important than having 'most of the latest values'. Relational databases used with transactions are an example of that. Which in my opinion is also a great place to store state. Persisting to non-volatile memory has some advantages; The process is also killed on 'phone drops', battery changes, hardware failures, os updates, reboots in general etc, so the state does persist when saved to non-volatile memory. |
This comment has been minimized.
This comment has been minimized.
gitspeak
commented
Dec 28, 2018
|
@jsroest Your arguments are valid but IMHO not applicable to this particular issue since the "state" being alluded to here is transient UI state (e.g Scroll position, active selection(s), temporary text input, etc...). Keep in mind that restoring this type of data is only required in those situations where the user has not explicitly discarded the "Screen" but user-input can still get lost, like when a user switches to another app. There is no user-expectation to recover transient state upon power-loss much like there is no user expectation that the browser will scroll the last displayed webpage to the exact offset prior to a system power loss. Point being, the nature of the data considered to be "transient state" is significant and the discussion here (Android onSaveInsatnceState facility in particular) is about handling that type of data. |
This comment has been minimized.
This comment has been minimized.
jsroest
commented
Dec 28, 2018
|
@gitspeak |
This comment has been minimized.
This comment has been minimized.
gitspeak
commented
Dec 28, 2018
Sorry, I don't know how to build applications for such customers, perhaps someone else can help ... |
This comment has been minimized.
This comment has been minimized.
|
@jsroest That's a great question but is not within the scope of this issue. Flutter is limited by what the platform itself can do and, as @gitspeak mentions, Android's Further recommended reading: https://developer.android.com/topic/libraries/architecture/saving-states [Note for instance how this article separates local storage from onSaveInstanceState] |
This comment has been minimized.
This comment has been minimized.
Zhuinden
commented
Dec 28, 2018
|
@jsroest if your customers expect the app to return where it was even if it was force-stopped, task-cleared or the OS was restarted, then that is something completely custom, and not what people are talking about here. onSaveInstanceState was saving the task state for a given task, but it was dropped on task clear or force stop or OS reboot. |
This comment has been minimized.
This comment has been minimized.
gitspeak
commented
Dec 28, 2018
|
@mehmetf I think this issue has indeed reached a level of verbosity at which it can be quite challenging to grok. With @LouisCAD blessing, I suggest you close this issue and open a new one which starts with your most recent summary (perhaps somewhat adapted to emphasis the problem statement and scope) along with back references to previous discussions. |
This comment has been minimized.
This comment has been minimized.
|
I concur but I will leave that up to @matthew-carroll; he will likely own this issue. |
This comment has been minimized.
This comment has been minimized.
jsroest
commented
Dec 29, 2018
|
Thanks for the feedback. I don’t want to push things, but I think I was not clear enough describing what I am looking for. I am not up to par with all the terminology used here on this thread. I see a lot of similarity between what I would like to see in Flutter and what @LouisCAD and @passsy are describing. My programs usually consist of the following parts:
The ‘variables’ data-structure only has simple classes with properties that are serializable. An important part of this ‘variables’ datastructure is the screenstack. (Simply List). The screen that’s on top, is the last screen the user interacted with. The one below is the one that the user navigates to when pressing 'back'. I can imagine that the OnSaveInstanceState can be used on the Android platform to trigger a serialization of this datastructure. Something similar has to be found on the other platforms (iOS, future: win, mac, web), but as @passy suggests other key points also may trigger this and that may be good enough. When the program starts, it will check if a serialized datastructure is available and if the current version of the program is the same as the version that serialized it. This can be done just by comparing version numbers. We do not want to load a datastructure that is not compatible. If this all adds up then the datastructure is deserialized and available in memory. The program loads the screen that is on top of the stack. The screen itself loads its transient state from the datastructure. Now we have a program that survives process deaths. (Or am I missing something? This definitely works on my past projects on windows mobile, Xamarin forms and asp.net. I think it should also work in Flutter). Sample application, where SXXX stands for a screen with a number. The number is only used to remind the programmer which screens belongs more or less together.
Sample variables datastructure
All that I would like to see in flutter is probably already there, except the recreation of the screenstack aka navigationstack. I have no idea how to recreate this object tree in memory. I am also not sure if I should want to recreate it. I can also make a navigation stack of my own and implement this in flutter as a single page application. The same as I have done in previous projects. But then I would loose a lot of built-in goodies from the MaterialApp and Scaffold classes, where the back arrow/button is the most obvious one. I realize that this does not automatically save all transient state. For example, selected texts, position of lists, position of a specific animation. But as the programmer, you can decide per screen what is needed to save. Although that is exactly what @LouisCAD is trying to prevent because all of the boilerplate code needed. Should I make a new issue or does this fit into this thread? Thanks again for the feedback, I really appreciate it. |
This comment has been minimized.
This comment has been minimized.
|
@jsroest Thank you for the detailed explanation. Please post this on StackOverflow with the flutter tag. You are essentially asking for a recommendation on how to structure and build your routes and how to save the last visited route in the persistent storage. You might think that solving this particular issue solves your problem too but that's not true. You can PM me at my github handle @ gmail.com if you don't understand why. |
LouisCAD commentedNov 12, 2016
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.