My application interacts with the Browser in a couple of use cases:
First Case:
Start MyApplication ActivityA from launcher.
Start MyApplication ActivityB using startActivityForResult (ActivityA's onSaveInstanceState is called).
Start Browser using startActivity (ActivityB's onSaveInstanceState is called).
Close browser or press Back. ActivityB is restored from the saved instance state bundle.
Second Case:
Start MyApplication ActivityA from launcher.
Start Browser using startActivity (ActivityA's onSaveInstanceState is called).
Browser calls back MyApplication ActivityA (i have no control over how the browser calls back, as in what flags are using for starting the activity. How it calls back is that i am passing it a callback URL (Google OAuth callback URL), and i have an intent filter in my ActivityA to intercept that URL.)
Start MyApplication ActivityB using startActivityForResult (ActivityA's onSaveInstanceState is called).
Start Browser using startActivity (ActivityB's onSaveInstanceState is NOT called).
Close browser or press Back. ActivityB can't fully be restored, because onSaveInstanceState isn't called in the previous step.
My questions would be related to step 5 of the second case, where the onSaveInstanceState is NOT called.
Reading through the Android fundamentals on lifecycle, as well as the onSaveInstanceState API, i sort of understand why it was not called. This is from the API for onSaveInstanceState:
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(Bundle) on B because that particular instance will never be restored so the system avoids calling it.
i.e. Because the flow is like
ActivityA -> Browser -> ActivityA -> ActivityB -> Browser
since there is already a prior Browser activity, ActivityB is deemed to be navigating back to the Browser, hence onSaveInstanceState is not called.
Am i correct with the understanding above?
In this case, should i really be using onPause instead of onSaveInstanceState? (From a laziness point-of-view, onSaveInstanceState is much simpler because i can just dump stuffs into the bundle instead of having to design/create a SQLite table which i'll have to do for onPause.) But what is the best practice for this?
It would seem then, if i am correct on 1 and 2, that saving state using onPause is much more robust than using onSaveInstanceState. What cases then, should we really be using onSaveInstanceState instead of onPause?
Related
My activity A starts one activity B in another app, and the user can spend quite some time on that activity. During this time, Android could possibly destroy my activity A (perhaps to reclaim memory), I am wondering:
If A starts B with startActivityForResult, and B calls setResult to return to A, If A needs to be recreated, will onActivityResult get called after A's onCreate?
If A starts B with startActivity, and B return to A by calling startActivity with flag FLAG_ACTIVITY_SINGLE_TOP, if A is alive, onNewIntent should be called. But if A is destroyed, will onNewIntent get called after onCreate?
I have read about android activity lifecycle posts on SO, but am not sure about this. And it's not easy to experiment on this.
1)It will, but the order of lifecycle calls may be a bit different than you expect. Be prepared for that possibility.
When you're in an an Activity (we'll call it A), and you invoke a subsequent Activity (B), perhaps as a result of clicking a button in A, and then RETURN to that prior Activity A, either by clicking the Back button or explicitly calling finish() from within B, it causes A to be completely rebuilt, calling its constructor and its OnCreate() method, etc.
Is there any way to prevent that from happening, so that it actually does return to the prior, already existing, Activity A?
Correct me if I'm wrong, but it should not call onCreate() here's a gross over simplification, but let's say activity's are managed much like a simple stack, let's call it AppStack
When a onCreate() for Activity A is called, the OS pushes the Activity Instance onto the AppStack
________ _________________
Activity|
___A____|_________________
When you click a button on Activity A, it launches a new intent to Activity B
Intent actB = new Intent(this, ActivityB.class);
and subsequently puts Activity A into Stopped state
When Activity B's onCreate() is called the OS pushes that Activity Instance onto the AppStack
________ __________________
Activity|Activity|
___A____|___B____|_________
Now if you call finish() or super.onBackPressed() in Activity B, the OS will pop() the Activity from the AppStack
________ __________________
Activity|
___A____|__________________
When the OS returns to the previous activity, it sees that it is Stopped and begins the process of Resuming it through onResume().
Now if there is some data that you require to be persistent, you can add it in by Overriding onResume()
Check out the activity lifecycle docs, for more info:
This is by design:
If an activity is paused or stopped, the system can drop it from memory either by asking it to finish (calling its finish() method), or simply killing its process. When the activity is opened again (after being finished or killed), it must be created all over.
See Activity Lifecycle. It's also why the Service class exists:
A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application.
It's not a typical scenario but when onCreate() is called when going back to that activity that means the Android OS kills it in the background.
Reason: Android is experiencing some memory shortage so killing some of the background task will be a must.
Is there any way to prevent that from happening?
No, you don't have a control over it, there are many reasons why its having a memory shortage e.g. other app installed that certain device is consuming more than expected. Although you can handle this use-case by storing the current information in onSaveInstanceState() and recovering the value from onCreate().
Calling finish() on ActivityB or pressing back will just destroy ActivityB.
ActivityA will not be completely rebuilt. This means it will not call onCreate method. It will just call onResume.
This is the normal behaviour.
However, on special situations, the system could destroy ActivityA (maybe because it needs memory to perform another task), so when you go back to it, the system will have to rebuild it.
To simulate this situation, there is a setting that you can check/uncheck, called "Don't keep activities".
If you have it checked, you will be simulating the situation explained above, it will always destroy the ActivityA as soon as it is not shown, and when you come back to it, the system will have to rebuild it calling onCreate.
I have an activity which starts various activities for result codes and on getting results in onActivityResult method it starts appropriate activity based on result code.
onSaveInstanceState is not getting called in Activity which started for result.
For example Navigation Activity starts Activity A as :
Intent intent = new Intent(this, A.class);
startActivityForResult(intent, Constants.REQUEST_CODE);
Then A finishes by setting result code so App will redirect to Navigation activity again and onActivityResult method will called.
So my question is: Why Activity A's onSaveInstanceState is not getting called at finish and navigation back to Navigation Activity ?
onSaveInstanceState() is only called if the Activity is being killed.
I don't know what exactly you want to do in that method, but you probably should move your code to the corresponding methods of the Activity Lifecycle.
from http://developer.android.com/reference/android/app/Activity.html :
Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
Also the method description for onSaveInstanceState() describes exactly your situation:
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(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(Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(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 method onSaveInstanceState() isn't called when an Activity finishes naturally like on a back button press. That's your app itself destroying the Activity. The method is only called if the Android OS anticipates that it may have to kill your activity to reclaim resources.
If the Activity then actually gets killed by Android, the OS will make sure you receive a call to onRestoreInstanceState() as well passing the same bundle you used to save your activity's state in onSaveInstanceState() method.
From the docs:
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(Bundle) or
onRestoreInstanceState(Bundle).
I had the same issue happening to me on a "drawer menu" part of a "main activity", because I had overridden the "onSaveInstanceState" method in my main activity but had forgotten the call to super.onSaveInstanceState() (which, as a result, would never call the "onSaveInstanceState()" method of my "drawer menu" which was part of this main activity).
In other words: make sure you don't forget your calls to "super.onSaveInstanceState()" where necessary.
I have two activities: ActivityA and ActivityB. ActivityA starts Activity B for result, and expects a certain resultCode. If it does not get it, ActivityA finishes. The idea is that this closes the application.
ActivityA also has an onResume method that does some stuff. I do not want this stuff to be done if ActivityA decides to finish in the onActivityResult method.
This works in most cases, except in low memory situations where android removes both ActivityA and ActivityB from memory and reloads them.
For example: ActivityA starts ActivityB. Background the application. Then android does its memory cleanup whatever. We can simulate that in DDMS.
Bring App To Foreground.
onCreate() called for ActivityB
Back Button to finish ActivityB
onCreate() called for ActivityA
onActivityResult() called for ActivityA
result is a cancel, call finish()
onResume() called for ActivityA
on resume does stuff
These last two steps are not desirable. Without the low memory issues, the normal workflow looks like this:
Bring App to Foreground
Back Button to finish ActivityB
onActivityResult() called for ActivityA
result is a cancel, call finish()
I have seen this on both a phone running Gingerbread and a Nexus 7 running Jellybean.
My first question: Am I missing something obvious in the Activity Lifecycle?
Failing that, is this the expected behavior from Android? Is there an elegant way to resolve this, or do I have to hack something, or should I try a different approach all together?
Thanks in advance. If anyone needs more information or code, just let me know.
You can declare a class member private boolean mShouldFinished. Set it to true in onActivityResult when called finish. In onResume check mShouldFinished and if true call finish.
My app an activity (which now a subclass on FragmentActivity, although I don't think that matters), let's call it Activity A.
In it, a button navigates away to (say) Activity B by starting it via Intent using startActivity() with no special flags.
Neither activity has any special flags (SingleTop) etc in the manifest, not calls finish() etc. i.e. nothing unusual.
Activity A's method onSaveInstanceState() get's called and I save some state info.
In Activity B I hit the BACK key and come back to Activity A.
It's onCreate() method is called, but the Bundle of "savedInstanceState" is null, and so I cannot reconstruct the state I had previously saved.
Any ideas what I am doing wrong, and how I can ensure I get the state back.
BTW: On a configuration change (say rotate), it all works fine....
It seems strange that onCreate() is getting called when you go back to Activity A just by pressing the back key. In general it should just show the existing activity without trying to recreate it. I also think it also strange that onSaveInstanceState() is getting called when you start the other activity. In fact, the documentation states that it probably won't call onSaveInstanceState() when starting Activity B:
An example: when onPause() is called and not
onSaveInstanceState(Bundle) is when activity B is launched in front of
activity A: the system may avoid calling onSaveInstanceState(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.
From here.
I think something else is going on.
Activity A was never destroyed, therefore onCreate() should not be called. Can you please put some code up?