Android activity lifecycle question: When to commit GlobalPreferences - android

I have a bunch of user preferences that are saved in a singleton object in my application. A sample model looks like this -
public class UserContext {
public static final String WEBSRV_IP = "TRIMMED";
public static UserContext instance;
// Contains username, password, if they're valid etc.
private LoginDataModel loginModel;
private ArrayList<FacilityDataModel> model;
private FilterDataModel filters;
private UserContext()
{
}
public UserContext getInstance()
{
if(instance == null) {
instance = new UserContext();
}
return instance;
}
// Getters and setters
}
So now, as the user goes through the application, I have a bunch of Activities that are created, go through their life cycle, utilize these variables and finish. (For example, in onStart() method, I check if the user is logged in before presenting the activity).
My question is, Android docs seem to recommend me to back up any context related data in my onPause() method. But onPause() also gets called every time I create a new Activity myself and it seems to be wasteful to back all the GlobalVariables up, only to restore them in the next activity's onStart() method. Is there a way to determine if the whole application has entered the background instead ? If not, when do you actually save all your Globals?
Thanks,
Teja.

I save preferences (with SharedPreferences.Editor.commit()) at the moment the preference value is determined, whether by the user or by the application code. I haven't yet come across a scenario when delaying saving preferences until sometime after they were determined made sense.

I think each application's case is going to be different. onPause() isn't a hateful idea if you need to save the state of that activity. What if the user moves to another of your own activities and then hits the home button? Your application will transition to stopped via onStop(). If they re-start your application there's a reasonable expectation (maybe) that the previous state was retained.
I save them when it makes sense to save them. For instance, I have an SMS application in which I let the user save the last message sent. I save their last message when the click send. I do nothing onPause() or onStop(), but I get everything done when the user would expect it to be done.
Developers traditionally hate if-logic, but a wise friend always reminds me, "if-logic is in the mind of the user." If I do this then this will happen ... in my case; if I send a message my message will be saved.

The only time onPause() is called and onStop() is NOT called... is if your activity is still actually visible beneath another activity that has only partially obscured it. Most of the time, onStop() will also be called, and if you know in your own activities structures you will always be completely obscuring each other activity, i would consider saving in onStop() and restoring in onStart().
Also remember, generally speaking whatever you save in onPause() should be restored in onResume(). What is saved in onStop() should probably be restored in onStart(). If onPause() occurs but onStop() does not... onStart() will not be called.
I should add as well that I agree with Bill Mote's statement, that you should save things when it makes sense to. Once you've got a solid understanding of the Activity Lifecycle framework, you can generally make a good choice.

Related

fragment - best way to save/restore model after onDestroy of activity?

i have a fragment that is hosted inside of an activity. when user prsses the back button i need to save the model data and have it available the next time user opens the fragment/activity. But just while in the app, it does not need to be persisted to disk. So for example if user destroyed the process, then there is no need to keep the model data, it can be fetched from network again.
what i have tried:
icePick and onSavedInstance calls but these dont seem to kick in when user presses the back button on the fragment. tell me if im wrong.
here is what i have implemented in my fragment:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("myModel", Parcels.wrap(myModel));
}
i am using the parceler library if that makes any difference. I can also convert the code to kotlin if required. when i hit the back button the fragment gets popped off the stack and the activity contain it calls onDestroy but im not getting any call back in onSaveInstanceState. Also when i check in onCreate() savedInstanceState is null. I have not overrided onSavedInstance in the activity, just in the fragment. What am i doing wrong ?
i had a though to use a database to do this, but i just need it while in memory and there should be a way to do this without a DB.
from what i learned if user hits the back button onSaveInstance is not called by the system:
If an activity is in the foreground, and the user taps the Back button, the activity transitions through the onPause(), onStop(), and onDestroy() callbacks. In addition to being destroyed, the activity is also removed from the back stack.
It is important to note that, by default, the onSaveInstanceState()
callback does not fire in this case.
source: here
#onSaveInstanceState of fragment is strictly coupled to activity lifecycle
According to doc
Called to retrieve per-instance state from an activity before being
killed
You operates only with fragments and activity is left untouched,
so this method is definitely can't be used in your case and shouldn't.
My suggestion is to use some kind of persistent storage though interface. It could be in memory storage (any type of singleton, like suggested in comments. It may be scoped to app or activity or to custom case (you have to control manually cache lifecycle) and injected with dagger, for example), shared-preferences based storage, database storage. It is easy to test if you follow dependency injection patterns & use structural pattern like MVP (but it is not a point of this question)
So store the data in the repository on change or in the onPause method (because it is the last guaranteed to call when screen is being gone). And restore it in onCreate

Android and background applications

I am unable to find out application is active(in background) or not.
For example, if user is in activity A (launcher/main activity) and navigates to activity B, then goes in background after some time or if android needs memory it'll kill application and next time if user wants to bring that app from background, or start it from home screen it will bring him to activity A, despite he sent it to background with activity B on top of activity stack.It would be logical that android returns application with where user left it.
Previously, I tried to handle it with static variable, but I found out that android kills application so that anything that I write to that variable is lost.
It would be nice, if there would be some notification from android OS before it kills applications. I was desperately searching for some workaround solution but didn't found anything that fills my needs.
I would be grateful if you could point me to some kind of solution.
UPDATE :
I have logic for restoring data but I don't know if application is in background or not ?
I want to do something like this
if(inBackground)
{
restore();
}
Try this:
On a BaseActivity that all Activities extend:
protected static boolean isVisible = false;
#Override
public void onResume()
{
super.onResume();
setVisible(true);
}
#Override
public void onPause()
{
super.onPause();
setVisible(false);
}
Whenever you need to check if any of your application activities is in foreground just check isVisible();
you can bundle A's state variables and send it to B, so that B can send it back to A later (if A gets killed) so you can restore A's previous state. This requires the data you want to save to be serilizable.
The suggestion (#Valentin) of using onSaveInstanceState will save the viewHierarchy and data stored in views. It's a bit confusing, because correct use requires calling the super functions in onCreate, OnSavedInstanceState, and not everything gets saved...just ViewHierarchy stuff (text in textViews, scroll positions, etc). You have to manually add/retrieve class fields to the bundle, so like #1, your data has to be serilizable.
If the data can't be serialized, you may try creating a reference to the data object directly in Activity B, so that when Activity A gets killed, the data object is hopefully preserved since it's referenced by Activity B as well, and B can pass the reference back to A.

How do tell if the Activity state has been destroyed and needs to be recreated

I have a list activity which does the usual list activity type stuff. All of the activity setup was being done in the onCreate() method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_streams);
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));
mActionBar = getActionBar();
mStreamListAdapter = new StreamListRowAdapter(this, R.layout.stream_list_row);
mStreamListView = (ListView)findViewById(android.R.id.list);
mStreamListView.setAdapter(mStreamListAdapter);
}
I do not have onStart() and onResume() overrides. I can successfully navigate out of this activity based on the menu buttons pressed and then navigate back to this activity. When I navigate away from this list activity via startActivity(newIntent), I do not call finish, but I do call finish in the newIntent activity, when coming back to this list activity. Everything works fine, unless either the ListActivity itself or one of the new Activities have been sitting in the background for a long time (i.e. I switched to another app). Long time is measured in hours. Sometimes (NOT always), when I come back to my app after this delay, the list activity does not display the list, the menu is not displayed either, just a blank screen with the activity title displayed in the ActionBar.
I realize that I need to handle coming back to the list Activity via onResume() (and maybe onStart()). But onResume() is always called after I navigate back to this list activity, how do I really know if the variables representing the ListAdapter and ListView have actually been destroyed by the OS and need to be recreated inside onResume(), I don't want to recreate them every time onResume() is called. Can I just check to see if they are equal to null? It's hard to test this, as this does not happen very regularly.
Thank You,
Gary
It sounds like you are missing some basic information about Android and Activity's lifecycle. First I want to mention how Android saves it's state. There are 2 important points here. When leaving an Activity it is at risk of being destroyed it calls the hook method
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("myKey1", 1);
outState.putString("myStringKey", "hello");
}
When this is called, this is a chance for you to save all the data that you'd like to persist. This could be ids, strings, arraylists, and nearly any other type of data.
Now that you've saved your data, in the onCreate method of your Activity you are passed back all of these values.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String myStr = savedInstanceState.getString("myStringKey");
int myInt = savedInstanceState.getInt("myKey1", -1);
}
// do the rest of your setup
}
On the first time onCreate is called savedInstanceState is null. To answer your question: "how do I really know if the variables representing the ListAdapter and ListView have actually been destroyed by the OS" this is how you can know. If savedInstanceState is not null, your stuff has probably been destroyed. However, I will say that anytime onCreate gets called that Activity was already completely destroyed anyway.
The other thing I will mention is Android is allowed to kill your backgrounded Activity whenever it needs to reclaim some memory. When it does this, it will call the onSaveInstanceState method for you. Android does not have to restart the application at the "first" or "MAIN" screen of your application. It can start off several Activities in if that's where the user left off. I mention this because it sounds like you're having issues whenever Android decides to create your activities after it clear's it's memory. The best way to make sure your application works well when this happens is to just rotate the phone. Rotate the phone from horizontal to landscape and/or back will cause the Activity to be destroyed and recreated with onSaveInstanceState called and passed back in to the onCreate. Based upon the information you've provided this is the best I can suggest for now.
Cheers.
I have finally figured out what was going on. I have 2 activities, ActivityA and ActivityB, and an Application object. Upon the application launch, ActivityA initializes some globals that are stored in the Application object and then starts ActivityB. When my app is restarted after being in the background for a long time, the onCreate() of the Application is called and then onCreate() of ActivityB is called. ActivityB relies on the variables in the Application object that normally are initialized by the ActivityA, but ActivityA is never called this time around and the globals are not initialized. I am still not clear why the ActivityA just hangs, instead of crashing with some message stating that the variables are null, which is what onCreate() in the Application object sets them to, but regardless, when this occurs, I need to go back to ActivityA() and reinitialize everything as I normally would, when the application is launched.
I now have a boolean in the Application object that indicates whether or not ActivityA has been executed after the onCreate() of the Application object has been called. ActivityB checks this boolean flag, and if not set, then simply start ActivityA to perform the on launch type initialization. I actually have to do this check in pretty much every activity.

Activity Life Cycle Methods and Saving State

I started reading around about the activity life cycle callbacks and saving state and there are quite a few things I don't understand - I'm writing an android app but I want to ask more general questions than how to do it specifically for the few activities etc I have at the moment, I would like to have a better overall view of how this works!
There are two groups of methods I have seen being used (I have seen one or two others but don't want to confuse myself even further...)
onPause, onResume etc,
and then the onSaveInstanceState ones.
What is the difference between them and the circumstances we should be looking to use them? I have seen some questions where a poster is using one of the normal life cycle callbacks, and is told to use onSaveInstanceState instead, so when should we be implementing onPause rather than onSaveInstanceState and so on. Some posts mentioned about methods being used for transient state only, could someone expand on that?
I have seen state being used to mean slightly different things - UI/View state and Activity state, what is the difference between the two?
I am also a bit unsure with what they mean by state, when we are saving state what kind of things are we saving exactly, could anyone give some quick examples (I don't mean actual code)? The android developer guides say that the android system automatically takes care of some of this, so what should we be concerned with? Bundle objects used by onCreate and onSaveInstanceState only store simple values, so what about more complex objects and arrays.
Thanks
protected void onPause ()
protected void onSaveInstanceState (Bundle outState)
Just by looking at it, onSaveInstanceState has an Bundle you can put your things you need to save in it. And get it back in onCreate(Bundle) or onRestoreInstanceState(Bundle);
Some important lines in the document:
This method is called before an activity may be killed so that when it
comes back some time in the future it can restore its state. Do not
confuse this method with activity lifecycle callbacks such as
onPause(), which is always called when an activity is being placed in
the background or on its way to destruction, or onStop() which is
called before destruction.
Android can destroy your activity or even kill your process at any given time (not likely when it is visible to the user though :-)). When the user navigates back to the activity, the data/info that was shown on the screen before he or she left it should be shown again.
The onSaveInstanceState callback allows you to do this.
Most of the Views already do this for you automatically. E.g. the current text in an EditText, the current scroll position of a ListView, etc. are all automatically saved for you.
However, there are some things that are not automatically saved for you. E.g. the current text in a TextView, the (changed) background drawable of a particular View.
Say, you show an error message after a user action fails. The error message is then shown in a TextField and this TextField's background becomes red (i'm just making this up here :-)). When the user leaves the activity while this error is shown (e.g. presses Home button), the activity is destroyed, the error message and the red background won't be shown again when the user comes back to the activity.
This is where onSaveInstanceState comes to the rescue.
You can save a String in there that containts the error message. Then when the activity is re-created, the Bundle savedInstanceState of the onCreate is not null and you can query it for the error message. If this message is not null/empty, call setText on the TextView for the error message and make that TextView's background red.
try to use this code to save state
#Override
protected void onSaveInstanceState(Bundle outState) {
State s = new State(yourTextView.getText().toString());
outState.putSerializable(State.STATE, s);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
State s = (State) savedInstanceState.getSerializable(State.STATE);
yourTextView.setText(s.getYourTextViewText());
}

Android: saving application state when going to the "background"

I have a complex Android app with a decent amount of application state that needs to be persisted (in a specific format). Writing out the data is not an instantaneous operation and so it would be ideal to minimize persisting it unnecessarily.
Because of the persistence overhead it is not practical to persist the state every time it is changed. Ideally, persisting state would be triggered when the app is "backgrounded" by the user tapping the 'home' button or tapping the 'back' button on the app's root Activity (or by an incoming call, etc.). This minimizes persistence overhead while maintaining state consistency from the user's perspective. The question is how can you detect if the app is being "backgrounded"?
The Activity lifecycle calls (onStart, onResume, onPause and friends) don't help as the app has many different activities, any one of which could be active when the user taps 'home'. Furthermore, the calls get called when Activities get pushed, popped (and bottom killed) on the Activity stack and so they don't reflect if the app is going away or not.
So how does an app detect when it is going to the background?
If you want to persist some state when any of your activities goes to the background you could always extend Activity, add two abstact methods which you call in onPause and onResume. Then each one of your Activities which extends this new abstract class would be forced to define saveState() and loadState(). These methods could define what to save and load for each activity.
That is just an example of using inheritance to force your programmers to implement otherwise overlooked methods and techniques methods. You can just tell your programmers, if you ever need to save the state of an activity just extend this type of activity and then the IDE will put them on the path of your design.
package com.yourcompany.yourpackage;
import android.app.Activity;
public abstract class ActivitySaveState extends Activity{
#Override
protected void onPause() {
super.onPause();
saveState();
}
#Override
protected void onResume() {
super.onResume();
loadState();
}
public abstract void loadState();
public abstract void saveState();
}
Also you could instantiate some of the state saving mechanisms for them in the super class (i.e. web service endpoint, DAO or w/e your persistence unit maybe.
#Override
protected void onResume() {
super.onResume();
saveState();
CustomDataAccessObject dao = new CustomDataAccessObject("Activity3");
loadState(dao );
}
public abstract void loadState(CustomDataAccessObject dao);
As far as I got in Android there is no concept of application going to background as a whole. Your application is thought of in terms of a federation of activities/services/... working together. And potentially you could configure it so that it can start from different activities, in different processes, in different tasks, so it's (almost?) impossible to figure out when your application goes to background.
Apart from this, to your question:
MyXxxActivity.onPause is the last safe chance you have to trigger the saving of data that has to be persisted across different "runs" of your application. This is not transient application state, instead it's stuff that goes to DB/File System storage and that you want to retrieve again in the next run.
I assume the application state you mention falls into this category.
So your saving must be triggered from each activity's onPause. If there's any state shared across different activities (e.g. in the Application), it should be collected from each onPause. Or its saving should be triggered as well from each onPause.
If the saving operation takes time, you should not run it on the main (UI) thread. Instead you should hand it off to another thread. Again, assuming this is important state that you do want to be persisted, then it's best to hand this work to a Service (as opposed to an AsyncTask e.g.).
The Activity lifecycle calls (onStart, onResume, onPause and friends) don't help as the app has many different activities
Your activities should be independent enough that you shouldn't need to know when your entire application is in the background. It sounds like you have other design issues that we can't help you with without more specifics.
I suggest managing your data on an activity-by-activity basis. This is suggested in the android docs.
For any activity that alters your persistent state, save that state when leaving the activity.
From http://developer.android.com/reference/android/app/Activity.html
onPause() is where you deal with the user leaving your activity. Most importantly, any changes made by the user should at this point be committed (usually to the ContentProvider holding the data).
This thread is pretty old, just to say onSaveInstanceState of activity is triggered when app is on background or when the user press home button on device. So there, you can save state, on load it on onrestore and so on...

Categories

Resources