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..
Related
I have the following security requirement for my app :
Verify that the app removes sensitive data from views when backgrounded.
My question is, Does Android remove data from views when backgrounded ?
What i know as per android documentation is that :
By default, the system uses the Bundle instance state to save information about each View object in your activity layout (such as the text value entered into an EditText widget).
One solution is that i can clear all views then use below approach to save state
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
} else {
// Probably initialize members with default values for a new instance
}
// ...
}
But i have no idea of whether Android os clears data in views.Any help will be appreciated.
Thanks
Android does not automatically recycler your view as soon as your application moves to background.
The system kills processes when it needs to free up RAM; the likelihood of the system killing a given process depends on the state of the process at the time.
It just recycle views if it decided to kill them or if it's in need for memory. Or upon a configuration change.
A user expects an activity’s UI state to remain the same throughout a configuration change, such as rotation or switching into multi-window mode. However, by default the system destroys the activity when such a configuration change occurs, wiping away any UI state stored in the activity instance.
Overall I think you had the right approach.
And just use onStop() (and not onPause() because of some Dialogs and other intercations that removes focus from your Activity) to clear your views if you want to.
Take a look at the android activity lifecycle https://developer.android.com/guide/components/activities/activity-lifecycle
One way to make sure all your data is cleared is to override the onPause function and clear your data manually. onPause is called when your application moves to the background.
No, it does not remove data because, according to the lifecycle of Android, when you do background the views, onPause() and onResume() is called and your views and its instances are in onCreate() method.
So, by launching the views from background, it does not remove your data of views.
Note: Even if the system destroys your activity while it's stopped, it still retains the state
of the View objects (such as text in an EditText) in a Bundle (a blob
of key-value pairs) and restores them if the user navigates back to
the same instance of the activity (the next lesson talks more about
using a Bundle to save other state data in case your activity is
destroyed and recreated).
the same instance of the activity
how it could be exact instance when it is destroyed and recreated , isn't it going to be a new memory block (another instance ), could anyone help me to clear this point ?
Unfortunately the documentation isn't very clear in a lot of areas. This is one of them.
In theory, if Android were to destroy your activity while it is stopped, then it would call onDestroy() and the GC would reclaim the memory. In actual fact, Android never destroys individual activities to free up memory. What it actually does is it kills the entire OS process instead. In this case, onDestroy() never gets called on any of the activities in the process. The GC doesn't bother to clean up anything, because the VM (Virtual Machine) just dies immediately along with everything else in the process and the entire amount of memory in use by the process is reclaimed by the operating system.
When your user navigates back to your application, Android creates a completely new process for your application and then it will create a new instance of your activity. In this case it will, of course, get a completely new block of memory for the instance. You will also see the constructor get called and onCreate() will also be called. However, since Android saves the state of the activity's views, that state will be restored by the activity by the call to super.onCreate().
There are a few cases where Android will destroy an instance of an activity and create a new one automatically. This is done, for example, during a configuration (ie: orientation) change. In that case, Android calls onDestroy() on the old instance and creates a new instance of the activity. The new instance gets the saved state of the old instance and can therefore restore the state of the views as able. In this case, since a new instance is being created, it will, of course, have a different address in memory.
Once a component is destroyed, it is effectively dead and the memory it is using can be reclaimed by the GC. Android will never reanimate a dead instance.
Hopefully this clarifies the situation for you.
EDIT Add more details
Android keeps track of tasks and the activities in those tasks in its own internal data structures. For each activity, it keeps a copy of the Intent that started the activity, the most recent Intent sent to that activity and it keeps a Bundle containing the saved state of that activity. Android calls onSaveInstanceState() on an activity to give the activity a chance to save anything that it will need to restore the activity if Android decides to kill it. The default implementation of onSaveInstanceState() saves the state of all the activity's views. Your implementation needs to save anything else that the activity will need to restore itself if it is killed and recreated by Android (for whatever reason). Your activity's private member variables and static variables are not automatically saved and restored in the Bundle, so if you need these to be able to recreate your activity properly you must save them yourself using the Bundle provided by onSaveInstanceState(). Static variables will stay around for the lifetime of the application's process, but since Android can kill the process (to reclaim resources) at any time, without warning, you also cannot rely on static variables always having the expected values (in the case where Android has killed and recreated your process).
If your activity (or process) is killed by Android and the user later navigates back to your activity, Android uses the information it has in its internal data structures to create a new instance of the activity. It then calls onCreate() on the new instance passing the saved instance state Bundle as a parameter. This can then be used to restore the activity to the state it had before the original instance was killed. Android will also call onRestoreInstanceState() after calling onStart() so that you can also do your restoration of state in that method if you don't want to do it in onCreate().
Note: Remember that anything you want to save/restore in a Bundle must be either a primitive (int, boolean, etc.) or it must implement Serializable or Parcelable.
The documentation of onSaveInstanceState() contains some useful information about this as well.
The state is stored, but the activity is destroyed to save memory. So, when the user is in an Activity A and goes to the Activity B, the Activity A is destroyed, but the state is stored. When the user presses the back button, the state will be loaded and everything will be restored.
The system does it when the activity call onStop and onStart. I didn't make any test, I'm just saying what I understood.
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).
At the moment I'm a little bit confused about the lifecycle management in Android. There are at least 4 possibilities to resume retained data after an Activity comes back to the foreground:
Android handling: If there is enough memory, Android stores and resumes the important data (checked radio buttons, text of EditText,-... and so on) after Activity restart, the user has the same state as before as the Activity went into background.
onPause, onResume: Overriding onPause and save the important data to a database or text file and resume it when onResume is executed next time.
onSavedInstance(Bundle), onRestoreInstance(Bundle): I can save the data as key-value-pair into bundles and restore them after onRestoreInstance is executed.
onRetainNonConfigurationInstance(), getLastNonConfigurationInstance(): I handle all my storage issues in one big object and read getLastNonConfigurationInstance() out when onCreate is executed.
Although it is confusing which approach is best, I guess it relies on development experience to know when to use which possibility. If you have some good examples for each I would be glad, but this is not my question. I wonder how to deal with all that when I have different Activities and one Activity will be killed by Android when it pauses in background:
In my case I have a MainActivity and a MessageActivity. The MessageActivity consists of a ViewSwitcher which consists of two states. State one is a radio button choice list. State two is an EditText with two buttons (send and abort). When I monkey test each state, hit the Android home button, and restart the application, the right Activity with the right state and the old data comes into foreground, when I leave the handling to Android. So that works.
But what happens when Android destroys the MessageActivity in background:
If I use the Android way, the data is lost and I guess MainActivity (instead of MessageActivity->state(1 or 2)) will start next time after I relaunch the application (is that correct?). So when I'd like to keep the data of MessageActivity, I have to use one of the other three possibilities.
How to do that neatly, when the application entry point (so the MainActivity) differs from the last active Activity. The problem is that I have to resume a special Activity with a special state of ViewSwitcher. I could start MessageActivity out of MainActivity with startActivity(Intent) in onStart() or onResume() method (because MainActivity is probably the entry point) but then I run into a lot of logical problems in Lifecycle management. Due to this fact I don't think that this is the right way to do that.
But, what's the right and best way to do that?
I guess MainActivity (instead of MessageActivity->state(1 or 2)) will start next time after I relaunch the application (is that correct?)
No, I don't believe this is correct, depending on what your code does in onCreate(). It certainly doesn't need to be correct if you go about things the right way. A simple way to test this is to rotate your screen, which recreates the running activities, unless you have overridden the default configuration change behaviour.
I recommend reading this section in the android docs carefully:
http://developer.android.com/guide/topics/fundamentals/activities.html#SavingActivityState
In particular:
even if you do nothing and do not implement onSaveInstanceState(), some of the activity state is restored by the Activity class's default implementation of onSaveInstanceState(). Specifically, the default implementation calls onSaveInstanceState() for every View in the layout, which allows each view to provide information about itself that should be saved. Almost every widget in the Android framework implements this method as appropriate, such that any visible changes to the UI are automatically saved and restored when your activity is recreated. For example, the EditText widget saves any text entered by the user and the CheckBox widget saves whether it's checked or not. The only work required by you is to provide a unique ID (with the android:id attribute) for each widget you want to save its state. If a widget does not have an ID, then it cannot save its state.
What this means is, that so long as you don't force any UI state in any onCreate() calls, your activity stack and UI state will be restored.
Personally, my preferred approach is to keep as little state as possible in member variables of my activities, saving and restoring it with onSave/RestoreInstanceState(), and relying on the default implementations to save the rest of the UI state (text box contents, etc). Data that should persist between sessions I commit straight to my DB or preferences as soon as it's changed (e.g. in the on-click handler). This means I don't need to worry about the activity lifecycle for that. As much as possible, my UI just presents a view of the data in my DB (using CursorAdapter etc.).
Edit:
Regarding restoration of the whole activity stack:
When the user leaves a task by pressing the HOME key, ... The system retains the state of every activity in the task. If the user later resumes the task by selecting the launcher icon that began the task, the task comes to the foreground and resumes the activity at the top of the stack.
(See http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html)
It's not my attempt for a best answer, but it's too long to get in the comments section.
First I will suggest not to rely on the "Android way" - this will result in inconsistent application behavior depending on the free memory of the device - bad practice.
My suggestion is to save your state-dependent data in key-value pairs in SharedPreferences, every time you go into onPause() in your MessageActivity. Store a flag in SharedPreferences, which indicates which was the Activity that was last opened (if you only have two Activities you can easily go 0/1 or true/false flags).
When you re-launch your application, it's normal to start the Activity marked in your AndroidManifest.xml as "entry point". So naturally you'll check the flag in onResume() in your MainActivity and start the other Activity if needed. In MessageActivity's onResume() check the values in SharedPreferences and fill in what's necessary...
If your application is "resumed" to the last Activity in the ActivityStack this will call onResume() in the last Activity in the ActivityStack.
The way I have handled an issue like this in the past, is to have a service running in the background, which handles the flow of information from different activities via either Intents and listeners (preferable, since they are the most easily decoupled solution), or if you are extremely careful, and the only viable solution for some reason is to store the data through direct property access or method calls, you can use static properties/methods on the service class as well. However, I would strongly recommend using the Intent/listener method as it is generally more flexible, thread safe, and decoupled. Additionally, it is wise to make sure that not much is happening at any point in time (in other words, only use this service for Intent handling) when it's not needed, otherwise the Service will tend to hog CPU time as well as RAM, when it's not really needed.
Some resources to look at when it comes to this approach would be IntentService and its related classes, including the superclass, Service. IntentService, however, it is worth noting handles a few more things about async Intent processing, etc that Service does not automatically come with.Hope this helps you!
login.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String name=username.getText().toString();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = settings.edit();
editor.putString("username", name);
if(name.equals("xxx")) {
Intent intent=new Intent(currentactivity.this,nextactivity.class);
intent.putExtras(bundle);
startActivityForResult(intent,0);
}
}
});
So I have the following:
A Common class that many of my Activities access in my android application, via setting the class in my manifest:
<application android:name="com.dev.games.phraseparty.Common"... />
Now, within this class I have several objects that are constructed to preserve application state and common services to perform applications that are constructed in the Common constructor
ie
GameStateVO gameState;
public Common()
{
gameState = new GameStateVO();
}
My problem is that my Activity has an Admob ad. When the user clicks on the ad, it calls the webbrowser intent to open the ad URL in a webbrowser.
Now, when I click back from the webbrowser launched by admob, it takes me back to the caling activity and the OnCreate method is called.
This activity then gets a null pointer exception because it will do something like:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Common common = this.getApplication();
//null pointer here since the game state VO is null since the Common has lost its state.
int score = common.getGameState().getScore();
}
If you have no active foreground activity, then your process is ripe for shutdown by the OS to obtain more resources. The browser app in particular i've noticed uses a lot of resources and can quickly lead to background activities and later processes being killed off. Having a service can help keep your process around, but even then it can still be killed off if needed. I think you'll need to use the activity lifetime cycle method to save & restore your state. See the process lifecycle section of the docs for more info
You might want to look into implementing the onSaveInstanceState method: this lets you store any relevant information before it gets killed off. See http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState%28android.os.Bundle%29 for the actual call you need to implement, and Saving Android Activity state using Save Instance State for a quite excellent example.