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.
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 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.
I'm working on an application which has a few Activities. One Activity starts the next one. To share some Values I'm using a custom implementation of Application (I'm talking about android.app.Application) called MyApplication.
As we all know, the Android system kills an app, if it needs more space. But this leads to a problem:
I open my app and use it just like any other app
I close it (Home Button) and use other apps
The system will kill my application because it's wasting memory
When I reopen my App, it wants to open the last activity I used and I get a force close, because the values in MyApplication are null
The strange thing is, that the system destroys my Application, but it seems like it keeps the Activity. I don't really understand why this is so because the Application doesn't seem to have a life cycle.
What I want to have:
When MyApplication (whole Application, not only the activity) gets killed, I want the last activities to be killed too. So when I reopen the App, it starts the main acitvity provided by the manifest.xml.
or
The values in MyApplication are persisted and don't get lost if the Application gets destroyed. (I'm talking about a few objects so i think the shared preferences won't work).
I don't want to use a service to bind my activities to, but is there a similar way to tell the system that my last used activity depends on the application-context?
I hope you understand what my problem is and someone can help me out with this.
The right way to do things would be to save your application state.
Override the onSaveInstanceState(Bundle savedInstanceState) method to save your state and onRestoreInstanceState to retrieve it.
If you need to save large sets of data consider using a SQL database
You should make sure the app closes and restarts the way you want it to in the onPause() , onResume() and onStop() methods. Check out savedInstanceState which can save the state of the app (and restore it, when it's sent as a parameter to onCreate)
In your custom implementation of Application, add a Flag say :
public boolean appContextExist = false;
On your first Activity set the flag to true,
Override onCreate and onResume method on your Activity which need the contexts, add following :
MyApplication myApp = ((MyApplication) getApplicationContext());
if (!myApp.appContextExist) {
// Code to return to start activity here
}
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 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...