I use a single static class in my code that defines a static field which I'm reusing between Activity onStop/onStart invocations. Here's a scenario:
User clicks on "Authorize" button (static data is initialized)
Activity is stopped and web browser is called
Browser executes callback and Activity is restored (static data is reused)
At least one of my users reports the failure at step 3 which I cannot reproduce but which looks like reset of static data
Any suggestions?
That is not safe. Your process can be killed between onStop and onStart, so all static data will be gone. In fact your activity can even be killed before it gets to onStop. In your tests the process was not killed, but it was for the user. See the Android activity life cycle which has a nice flow chart showing the possibilities.
You need to store the data some other way, in prefs or database for example.
If this static data is related to activity which you have just stopped - you could use normal non static fields + onSaveInstanceState method.
#Override
protected void onSaveInstanceState(Bundle outState) {
// ... save your Serializable data here in outState bundle
super.onSaveInstanceState(outState);
}
The case would be:
you close your activity and go to browser (onStop is invoked)
system kills your application process (onSaveInstanceState is invoked
where you save data)
User navigates back to your activity (onCreate is invoked with
savedInstanceState parameter)
In most cases 2nd point will not occure. System can but doesn't have to kill your app process. When it doesn't - you will not get onCreate method but onStart and onResume methods and your fields will be unchanged.
Related
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.
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.
Hi guys i have an app which is almost totally complete.It is a music player.It plays music from a service.
When i press back button that activity which started the service is obviously destroyed.(i want the activity to be destroyed so that the user can navigate other activities.The reason i am telling this is because singleton|singleinstance etc won't solve my problem) But i don't know how i can recreate the activity from the notification.
Also when the activity which started the service is visible(and not destroyed) then too if i click on the notification, my app stops unexpectedly.And hence i have two problems to solve here.
I am using a global variable which is a list and hence i don't think i need to save data before destruction.I think i know the concept how the activity state can be restored and recreated but i think i am not totally aware how it can be done.I am not sure in which method the activity state should be restored and how can it be restored ??Also i am starting the service in onCreate method.Should i change it to some other method?Can starting the service in onCreate be a problem when restoring the activity ?
Extra Note :Global Variables are available throughout the application and are independent of an individual activity,as they extend Application.Also Bundle will only help when the activity is destroyed by the system.When Pressing the back button we explicitly destroy the activity and hence data cannot be saved into the bundle as onSaveInstanceState is never called by the activity.
Activity state should be restored in onCreate. The onCreate method gets a Bundle as parameter which contains key-value pairs of data. If this parameter is null, it means that no previous state was saved. Just check if it's not null, and restore things if necessary:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
...
if(savedInstanceState!=null) {
// Restore activity state based on saved data!
}
}
Of course you need to save data if you want to use it at restoration. System automatically calls the activity's onSaveInstance state method in which you can put data into the bundle.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("key", value); // Store data
}
Please note that this all applies to the case when system destroys your application automatically. (For example, when other apps in the foreground run out of memory.) This mechanism is to get your activity back to a state in which the user last saw it.
If the data is consistent and reflects with the Service, then you need to bind to the Service and get the data from the IBinder (which can wrap a Service object). I, personally, prefer this method because I simply don't like overriding the Application.
An alternate method is to save data via the SharedPreferences. Save it your Activity state in onPause() and restore in onCreate(). Assuming you're not saving hundreds of items, it is very low overhead. This should only be used for data that reflects the state of the Activity itself.
You can play and stop your player based on Activity life cycle like
OnPause -you should call player.stop()
onresume you should call play and
ondestroy you need to release your player instance.
call a thread inside your service to get data from server so that it will work smoothly.
you should release mediaplayer instances by calling release or reset .
You can use Pending intent in notification so when you will see notification then you can open your activity whatever mentioned in pending intent.
When OS will completely forget the state about an Activity and all information relate to an Activity ( any records about an Activity) ?
In other words when it will make Bundle to be a new instance?
I found the below explanation but it does not explain this "Killing bundle" point?
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/Activity.java#Activity.onSaveInstanceState%28android.os.Bundle%29
Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(android.os.Bundle) or onRestoreInstanceState(android.os.Bundle) (the android.os.Bundle populated by this method will be passed to both).
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(android.os.Bundle) or onRestoreInstanceState(android.os.Bundle).
Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(android.os.Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(android.os.Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(android.os.Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.
The default implementation takes care of most of the UI per-instance state for you by calling android.view.View.onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(android.os.Bundle)). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation, otherwise be prepared to save all of the state of each view yourself.
If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause().
There are many cases where this could occur:
Here is a small topic on reference mentioning different
use-cases about recreating activities.
If you want more control over the application life cycle:
Take a look a the application object for more information.
Personal advice:
I think you should not care about when/what, This is something the
android system will manage for you, your code should be optimized to
handle all usecases (when the bundle will be null or not). A bundle
should only be used to handle state-keeping, if you want to
persist data you should take a look at fileI/O, SQLlite or shared
preference.
I think my ideas on activity lifecycle and bundles
are a little confused,can you help me?
Let's suppose the user opens activity A from home screen,
activity A "calls" activity B which fills the screen.
On this event onSaveInstanceState() is called on activity A then onPause() and onStop().
Since there are too many apps currently running on the system,
andorid decides to kill the process hosting activity A.
When the user navigates back to activity A,onCreate() is called an we can
use the bundle ( setted during the last call of onSaveInstaceStae() ) to restore the state.
then onStart(),onRestoreInsanceState()
and onResume() are called,
am I right?
Then lets suppose the user presses back key to exit from activity A
onPause(),onStop() and onDestory() are called in sequence on activity A (the call of onDestroy() could be postponed though)
onSaveInsanceState() should not be called in this scenario.
When the user opens again activity A later on then the bundle
passed to onCreate() is null,right?
Now Suppose the user rotates screen
onSaveInsanceState() ,OnPause() ,OnStop(), OnDestroy() are called
then onCreate() with bundle setted by the last call to onSaveInsanceState(),
and then onStart(), and onRestore().
am I right?
My guess is that:
when the user creates an ativity,the bundle passed to onCreate() is always null and onRestoreState() is never called,but when the system creates it , for instance when it killed the activity because of low memory or because of a rotation event,the bundle passed is the one setted by the last call of onSaveInstanceState().
Is my guess right?
thanks and sorry for my poor english.
P.S. : I think onRestoreInstanceState() is passed the same bundle is passed onCreate() but typically state is restored using onCreate().
Interesting question - never thought about it.
Take a look at the documentation, onCreate() and onSaveInstanceState().
This answers at least your question what Bundle will be supplied to onCreate().
Unfortunately there is no exact definition on which events onSaveInstanceState() is called, but I guess it is called by default in all relevant situations (whatever they may be...), but you can find out for some situations (e.g. rotating the screen) by putting a Log.i() to LogCat.
onRestoreInstanceState() is passed the same bundle is passed onCreate() is right, and the system restart the activity by call onCreate() and also call onRestoreInstanceState(),the bundle will be null if get from onCreate() when the activity first started.
Since there are too many apps currently running on the system,
andorid decides to kill the process hosting activity A.
This is a very common situation. Furthermore - you can emulate this action using developers option.
In this case each activity, that was move into background, will be automatically destroyed.
About Bundle in OnCreate .
I can get from my memory only two cases when OnCreate will be called with non-null Bundle. First - described above. Second case - screen rotation.
When you launch app Android calles
onCreate
onStart
onResume
After that lets doing screen rotation. Android will call
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
The more information you can find on topic about recreating