My app is made of two activities, A and B. I'm considering this sequence of steps:
Activity A is started.
A launches B [A is paused, B is running].
B launches a map intent [A and B are both paused now].
Now the user is using the maps application and the system decides it needs more memory. Can the system kill only one of my activities for memory, or will it always kill all activities in a "process" in this situation?
Both activities share some static data like:
class Data {
public static String mName;
public void save() {
// write to file: mName;
}
public void load() {
// mName = read from file;
}
}
ActivityA.mTextView.setText(Data.mName);
ActivityB.mListView.addText(Data.mName);
so when any activity in my app gets onSaveInstanceBundleSate() called, I call Data.save() to write it to disk. Now the question is, in an Activity's onCreate() method, should I simply check to see if Data.mName == null, and if so, assume the Activity is returning from a kill state, and try restoring from disk again? I'm unclear when this restoring should be done, considering Activity A may or may not still be alive etc. - and I don't want to corrupt state if Activity A is still alive but B is coming back from a kill state,
Thanks
Thanks
Probably the best solution is to move your static data to a Service. That way the data can be saved and restored when android shuts down the Service rather than when Android shuts down either of the individual activities using the data.
Without using a Service (or alternately a Content Provider, or even overriding Application although that seems to be frowned upon), you have to manage that lifecycle yourself, which as you've seen can be tricky.
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..
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.
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.
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...
In my Android app, when a user tries to transition from one activity to another, there may be some global state that indicates they need to complete some other action first.
To accomplish this, I've written a class with the following code:
private static WeakReference<Activity> oldActivityReference;
private static Intent waitingIntent;
public static void pushActivity(Activity currentActivity, Intent newActivityIntent) {
Intent blockingIntent = ThisClass.getBlockingActivity();
if (blockingIntent != null) {
ThisClass.oldActivityReference = new WeakReference<Activity>(currentActivity);
ThisClass.waitingIntent = newActivityIntent;
currentActivity.startActivity(blockingIntent);
return;
}
currentActivity.startActivity(newActivityIntent);
}
When the blocking activity finishes, it calls ThisClass.blockingActivityFinished(). That will check to see if the weak reference to the old activity still exists and, if so, launch the original intent from that activity. If not, it will launch the original intent from my application's context.
My question is,
Does this sound sane? Are there any potential memory leak issues with this technique? Is there a better way to accomplish this?
EDIT -
To be clear, the types of events that might trigger an interruption are 1) a server ping indicating that the current app version is deprecated 2) any server RPC indicating that the user's credentials are no longer valid. I do not want to add logic to every Activity to handle checking for these, and resuming business as usual once they complete. That is a violation of DRY, and error-prone in a team environment.
Does this sound sane?
I'd never use this technique. Mutable static data members are dangerous, WeakReference notwithstanding. In particular, I'd expect this to fail if the user does the unthinkable and, say, uses their phone as a phone, or otherwise leaves your application flow for an extended period of time. Your activities may be destroyed and your process terminated to free up RAM, yet the activities would remain in the task and might be reactivated. At that point, your state is whack, because the statics got nuked.
Are there any potential memory leak issues with this technique?
You're leaking an Intent.
Is there a better way to accomplish this?
For the purposes of the rest of this answer, I'm going to refer to your starting point as Activity A, the "some other action" as Activity B, and the desired end as Activity C. So, in your code, newActivityIntent is for Activity C, blockingIntent is for Activity B, and currentActivity is Activity A.
Option #1: Put the decision-making process in Activity C, rather than Activity A. Have Activity C check the condition in onCreate() and immediately calls startActivity() for Activity B if the conditions require Activity B to be shown.
Option #2: Leave the decision-making process in Activity A, but pass the boolean (e.g., true for "we gotta show Activity B") in an Intent extra for the startActivity() call for Activity C. Activity C checks the boolean in onCreate() and immediately calls startActivity() for Activity B if the boolean says so.
In these options, you avoid the statics.