I am having SearchCritiera object and i make it singleton and declare this variable as static,
now problem is if i left my application remain open for couple of hours that static object is removed by Android OS, how can i make sure static object should not be removed by the OS.
like i know there are few keywords like
Weekreference and softreference is there any strongreference keyword which can tell Android OS do not remove the reference ??
Don't use static references, even if your application remains in the foreground, these objects may be destroyed by the garbage collector (I've seen this happening a couple times now).
You can simply avoid this by storing them in your unique Application instance. That object is guaranteed to live as long as your app.
Sadly, you can't force Android to keep your application in memory. If the OS feels it needs more memory for a foreground application or service it reserves the right to terminate one or all of your Activities.
I'm assuming what's happening is your static object is being lost and re-created when you call it, meaning that it has lost its state. That said, if you have a reference to the object in a foreground Activity I'm a little surprised that it's getting lost.
The best you can do work around this is hook into the lifecycle events and save the state of your singleton object and then restore it when appropriate.
Unfortunately, there are no Application wide lifecycle events in Androd. Have a look at this question for how to save transient application state.
If i am not wrong when application remain open for long time data is released by the android OS, and while stopiong activity it will call "onSaveInstanceState" and when can i store searchritiera into this method and will it retrive back when "onRestoreInstanceState" is get called ?
private static SearchCriteria searchCriteria;
#Override
public void onSaveInstanceState(Bundle outState)
{
outState.putSerializable(WLConstants.SEARCH_CRITERIA, searchCriteria);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if(savedInstanceState != null){
searchCriteria = (SearchCriteria)
savedInstanceState.getSerializable(WLConstants.SEARCH_CRITERIA);
}
super.onRestoreInstanceState(savedInstanceState);
}
Related
I am not understanding how android activities are managed.
I have an activity and every once in a while i have noticed that, when my app goes into the background, android destroys whatever the current activity is (say Activity3) and several other singletons and objects etc. Thats fine. The problem is when the app is resumed then intuition tells me that since android has destroyed the activity and objects for memory or whatever, then android would just restart the app completely from Activity1 so all the objects and data members would get properly initalized.
NOT SO!
It seems that when my app is resumed, the Activity3 is recreated and onCreate is called with the same parameters as it was the first time (when it was called from Activity2) only this time all the singletons and other objects that were initialized in Activity1 and Activity2 are recreated with their default values and are rendered useless.
How is this a safe policy/technique? How can android just randomly destroy objects and activities and then when the user resumes just call onCreate on the recent activity and expect everything to be hunky doory and NOT have to go through the proper startup procedure/initialization?
UPDATE / SOLUTION
Thanks to the commentors for their excellent info.
ACCORDING TO ANDROID DOCUMENTATION
onCreate
Bundle: If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null.
THEREFORE what I ended up doing is I set TWO flags. One in onSaveInstanceState in the Bundle so to know that it is a valid Bundle set by me. The other in the class itself to determine if onCreate was called because of recreation or Auto-Rotation. And so in onCreate I checked to see if onSaveInstanceState is not null, check the Bundle flag, and check bInit (which defaults to false). If both flags are true then it means android dumped and destroyed our apps memory and the safest way to ensure everything is initialized again in a linear-style application is to just restart it and launch the beginning activity.
public class SomeMiddleActivity extends AppCompatActivity
{
private static boolean bInit = false; // only way it will be false again is if android cleared our memory and we are recreating
#Override
public void onSaveInstanceState(Bundle state)
{
// set a flag so that onCreate knows this is valid
state.putBoolean("StateSaved", true);
super.onSaveInstanceState(state);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
// this must be called first always for some reason
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
if (savedInstanceState.getBoolean("StateSaved", false) && !bInit)
{
// we were recreated... start app over
Intent intent = new Intent(getApplicationContext(), Startup.class);
startActivity(intent);
finish();
return;
}
}
bInit = true; // this will stay true until android has cleared our memory
.......
}
Although this has worked thus far, if anyone has a different suggestion let me know. I will be posting another article on this.
And FYI: the onSaveInstanceState(Bundle, PersistableBundle) version of onSaveInstanceState is never called ever so I dont know why they even implement it. (?)
#goldenb #Rishabh Thanks to goldenb and Rishabh for the insight.
Android, if destroys, also gives you tools to handle it.
Mobile devices have limited amount of memory which needs to be shared among Applications running simultaneously. Thus, smart resource allocation is necessary. The Apps running on foreground are used by End-User and gain high priority for better performance and user experience. Thus, applications running in background need to release the resources to suffice the memory requirements for foreground applications. Hence, background applications are destroyed (not completely) sometimes (in case of low memory).
Android Activities have Callbacks likes onSaveInstanceState() and onRestoreInstanceState() which enable you to save your current state of Activity (i.e., values of variables) when it is destroyed and retrieve them when the Activity is recreated.
You can get more information from here: How to save and retrieve the state of Activity using onSaveInstanceState and onRestoreInstanceState.
You can perform validations on the retreived state to ensure the Activity performs exactly as it was doing pre-destruction. You would find it very easy and logical once you get hands-on it.
Just giving my 50 cents on the issue. The correct way to deal with the issue of an activity being killed by the system for its resources in background is a common problem in android and according to Google the solution for this is:
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).
Emphasis is mine. But what this means is that the Android lifecycles are designed so that under normal conditions onPause should be called as an Activity or Fragment is sent to the background. They hint at this in several of the android documentation pages:
As your activity enters the paused state, the system calls the onPause() method on your Activity, which allows you to stop ongoing actions that should not continue while paused (such as a video) or persist any information that should be permanently saved in case the user continues to leave your app.
Also worthy of your attention: if you wish that views are restored during Activity recreation, you should have set the ID attribute of all views ;)
Note: In order for the Android system to restore the state of the
views in your activity, each view must have a unique ID, supplied by
the android:id attribute.
PS.
You were wondering why onSaveInstanceState(Bundle, PersistableBundle) is not called, one possibility is that you do not have the right activity attribute set
This is the same as onRestoreInstanceState(Bundle) but is called for
activities created with the attribute persistableMode set to
persistAcrossReboots..
Can somebody clarify please why I have such a weird behavior. Up to documentation the Bundle savedInstanceState which is set in onSaveInstanceState() is alive as long as application alive, so when it it in foreground or background. After the application is being killed the savedInstanceState instance is killed as well. Here is what I have:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (savedInstanceState != null) {
Log.i("Dev", "not null");
} else {
Log.i("Dev", "null");
}
}
Here is how I set it:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("bool", true);
}
Then, I'm starting the application in the emulator. After application is opened I click home button so the Launcher is visible. Then I kill the application's process using adb. After that I start the application from the list of recently used application expecting for "null" in the Logcat, but what I actually see is "not null", so my understanding is incorrect?
The Bundle is saved for as long as Android wants it to be saved/can save it. One of the "features" (quotes because it ends up being a bad idea as often as it is a good one) of Android is that applications are never exited (to the user's view). Their mechanism of doing this is the onSaveInstanceState- it stores the Bundle, and when the app is later reinitialized by some method (such as from the recent activities menu) it will pass that Bundle to the onCreate and let it re-initialize itself.
Of course this also causes problems. For example, if you save login info, exiting an application won't log you out. So a user can then just hand his phone to a friend to watch a video, thinking that he exited his mobile banking app and is safe, yet the friend can call it back up and recreate it. If your app has large data structures in static variables or singletons they will not be recreated unless you code it carefully. Apps that require activities to be explored in order can be restarted from the middle.
Now Android can choose to forget your Bundle. If you put several MB in it, I would expect android to forget it rapidly. But it will remember it for as long as it can.
Isn't it very clearly stated here ? Or do I missunderstand your question?
[..]To save additional data about the activity state, you must override the onSaveInstanceState() callback method. The system calls this method when the user is leaving your activity and passes it the Bundle object that will be saved in the event that your activity is destroyed unexpectedly. If the system must recreate the activity instance later, it passes the same Bundle object to both the onRestoreInstanceState() and onCreate() methods.
I mean for me this is also reasonable in most situations. Because when your activity / app is in the background and the android system closes it (let's say because it needs more memory), then it first saves the state. So next time the users opens your activity, you can restore it's previous state (and that may also exactly be what the user wants, since it wasn't him who closed the activity, but the system itself).
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.
I've been bothered by this "characteristics": When I use Back button to leave my app, I can tell onDestroy() is called, but the next time I run my app, all the static members of the Activity class still retain their values. See the code below:
public class HelloAndroid extends Activity {
private static int mValue; // a static member here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText((mValue != 0) ?
("Left-over value = " + mValue) : "This is a new instance");
setContentView(tv);
}
public void onDestroy() {
super.onDestroy();
mValue++;
}
}
The above code displays the left-over value in mValue, and it increments when the session ends so that I know for sure the onDestroy() is called.
I found a useful answer on this forum, and I understand in the above code mValue is a class member, instead of an instance member. But isn't it true that, in this particular case, I have only one single HelloAndroid activity, and so When he dies, everything is cleaned up, and the next time I come back, everything starts over again? (Or, is there some other misterious thing in the system still holds on to it after onDestroy() so that it just won't die???)
(The above is just a variable, what if it's a bunch of objec references? Each piece is a separately re-collectable memory. Is there a chance that GC collects some of them but not all-or-none? This really bugs me.)
The OS decides when things "go away." The onDestroy is there to let your app have a final chance to clean things up before the activity does get destroyed but it does not mean that the activity will, in fact, be GCed. Here is a good article that I recommend people to read that relates to creating an exit button. While it's not exactly what you asked about, the concepts will help you understand what's going on.
You don't just have the Activity though. You also have the application, and its process running in a Dalvik VM. Android will usually leave the application running in the background until it needs to reclaim the memory it is using for some other application. Your static member should remain in memory as long as the process is running. If you try running some memory-intensive application or forcefully closing the running application with some task manager, you may see the static value reset.
After going through an introductory Android programming book, I wanted to alter the example application in order to solidify my understanding of some topics that weren't really covered. In making the change, I made an error, but I'm curious why the error worked in some cases but not in others.
An activity within the application stores a series of questions in a Hashtable<Integer, Question>, where Question is a small class holding an int and two Strings. As originally written, the activity downloads the questions from a server on every onCreate(), so I wanted to implement onSaveInstanceState() to prevent some redundant downloads. onSaveInstanceState() saves the Hashtable into the Bundle using putSerializable().
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// mQuestions is a member variable of
// type Hashtable<Integer, Question>
if (mQuestions != null && mQuestions.size() > 0) {
outState.putSerializable(SAVED_QUESTIONS, mQuestions);
}
}
It worked perfectly for screen orientation changes even before I knew what a Parcelable was or how to implement one. I only knew there was a problem when I pressed the emulator's home key and the app silently, invisibly crashed with no LogCat output. The stack trace led me to look up Parcelable and make Question implement it.
My question isn't what I did wrong. The question is this: When the Question class did not implement Parcelable, why did the app crash only on pressing Home and not on a screen orientation change?
As far as I understand Android doesn't serialize an instance state when recreating an activity after configuration changes. That's why your code works. Persisted objects just don't need to be parcelable because they exist in memory only.
This looks like an optimization. Android knows that the process will not be terminated in this case and there's no need to save the instance state to a file. (In theory the process can be terminated during the configuration change and I don't really know how Android solves this problem).
But when the user presses Home key your app becomes background. And its process can be terminated in case of low memory. Android needs to save activity's state to a file in order to be able to restore your app and its activities in future. In this case the instance state is really serialized and saved to a persistent storage. And that's why your code doesn't work.
Process termination can occur at any moment so you can't rely on some implementation details. Just make instance state parcelable or serializable and you will not face this problem again.
Quoting Steve Moseley
Note that it is NOT safe to use onSaveInstanceState and onRestoreInstanceState, according to the documentation on Activity states in http://developer.android.com/reference/android/app/Activity.html.
The document states (in the 'Activity Lifecycle' section):
Note that it is important to save
persistent data in onPause() instead
of onSaveInstanceState(Bundle)
because the later is not part of the
lifecycle callbacks, so will not be
called in every situation as described
in its documentation.
In other words, put your save/restore code in onPause() and onResume() instead!
The app did not crash. It was simply shut down when the user clicked the Home key. That's why there was no output to LogCat.
Set a breakpoint in Activity.onDestroy() to confirm this. If I'm right onDestroy() will be called but onSaveInstanceState() will not, because onSaveInstanceState() is only called when the app is placed in the background state, not when it's shut down.
If you need to save the app state upon shutdown, place the code in onDestroy() and save it to something more persistent than a Bundle.
Barry