Say i have started activity A with new Intent(context,class) i thats has sensitive data in it, when is this intent gets destroyed? in particular, in what cases among the following would getIntent() in Activity A's code return the exact same intent i?
Press on the activity's task on android's task manager
App icon was clicked and the activity was recreated and brought to front
Ive tried it with my app, iand i get weird results... normally it doesnt get the same intent, but sometimes it seem that it does, so i am not sure whats going on, anyway If i can be returned from any of the upper options how to avoid it?
I think a glance on lifecycle of an Intent would be helpfull if any1 know of any documentation regarding this...
Say i have started activity A with new Intent(context,class) i that's has sensitive data in it, when is this intent gets destroyed?
as long as there's object/class that holds reference to your Intent object - it will not be garbage collected. the Activity (Activity A) holds reference to the intent that started it, so as long as Activity A object is not garbage collected - then i also won't be garbage collected.
important comment: onDestroy() activity callback and class distractor are to different things!!!
in what cases among the following would getIntent() in Activity A's code return the exact same intent i?
assuming you are not calling setIntent() explicitly:
1) Press on the activity's task on android's task manager:
if the activity was previously stopped in reaction to back button navigation or someone called finish() explicitly on it, then the activity passed on the onDestroy() callback. in that case - pressing on the "application" from the recent tasks manager would re-created the activity with a new intent from scratch, and thus - getIntent() would bring this new intent that don't contain your extras or other overloads.
otherwise (the activity was sent to background via home button, or other activity started on top of it) : when you'll launch it back from the recent task - it will be intent object with the original extras you passed it before...
2) App icon was clicked and the activity was recreated and brought to
front
basically the same cases I mentioned in (1) apply to (2) , but basically it depends on two more things:
the intent flags that the specific launcher you are using is overloading on the intent it creates when it launch your activity.
the launch mode and activity flags you overloaded on the intent that you used to start your own activity.
assuming you are not using any of the above, and you are using normal good functional launcher application - the behavior would be exactly as I explained in (1)
Related
We have crash reports from our App that we cannot explain. The crash occurs in MyActivity because an expected "extra" is missing from the Intent. We have extensive logging in our app and this is the sequence of lifecycle callbacks that we see when this occurs:
06:04:22.574#a.b.c.MyActivity.onCreate() with flags 0 a.b.c.MyActivity#80773a0
06:04:22.592#a.b.c.MyActivity.onStart() a.b.c.MyActivity#80773a0
06:04:22.596#a.b.c.MyActivity.onResume() a.b.c.MyActivity#80773a0
06:04:23.601#a.b.c.MyActivity.onPause() a.b.c.MyActivity#80773a0
06:04:23.614#a.b.c.MyActivity.onNewIntent() with flags 30000000 a.b.c.MyActivity#80773a0
06:04:23.654#a.b.c.MyActivity.onResume() a.b.c.MyActivity#80773a0
We log the object ID (in this case 80773a0) so that we can tell how many instances of a given Activity are in use.
You can see (due to the object ID) that there is only a single instance of MyActivity involved here. The Activity is created, started and resumed as usual. There are no special flags in the original Intent (we log the flags in onCreate() and onNewIntent()).
Approximately 1 second after onResume() we see a call to onPause(), immediately followed by a call to onNewIntent() and then onResume(). The Intent passed to onNewIntent() contains flags 0x30000000 which is FLAG_ACTIVITY_NEW_TASK (0x10000000) and FLAG_ACTIVIY_SINGLE_TOP (0x20000000). The Intent passed to onNewIntent() has no extras, which cause the app to crash.
We have double-checked and there is absolutely no place in our code where we set both these flags in an Intent.
We do not understand what is causing the calls to onPause(), onNewIntent() and onResume() and we do not understand why the Intent has this set of flags nor why it does not contain the necessary "extras". There is only one place in the app where this Activity is launched and the necessary "extras" are put in the Intent before startActivity() is called.
The Activity in question works correctly almost all the time, so there is no general problem with it. There must be some specific behaviour that causes this problem and we have exhausted our ideas about what it might be.
The manifest entry for MyActivity has no special launch mode specified.
If possible then please extract parts of code necessary to examine this issue because we're shooting blind.
What I can read in docs is:
An activity can never receive a new intent in the resumed state. You can count on onResume() being called after this method, though not necessarily immediately after the completion this callback. If the activity was resumed, it will be paused and new intent will be delivered, followed by onResume(). If the activity wasn't in the resumed state, then new intent can be delivered immediately, with onResume() called sometime later when activity becomes active again.
So it seems that your case is exactly matched in that part of docs!
I assume some basterd is calling startActivity with flag FLAG_ACTIVITY_SINGLE_TOP twice in a row.
I think this can happen for example if you do startIntent from button's onclicklisteners - fast clickers may be able to do it twice. Solution for that comes with RxJava debounce.
I have almost the same requirements as this excellent question:
Send data to activity with FLAG_ACTIVITY_REORDER_TO_FRONT
However, I'm using FLAG_ACTIVITY_NEW_TASK because each of my primary activities needs to run as a new task. In my testing onNewIntent isn't called in this case (even when activity already running). So how do I pass data to it?
I can get onNewIntent to be called, by setting launchMode to singleTask, however then I run in to this bug: Android: bug in launchMode="singleTask"? -> activity stack not preserved
I want to accomplish:
If no instance exists, create a new instance, as a new task, and pass it data. (this part already works, I can get the data in onCreate)
If an instance already exists, bring the task (entire task, not just root/one activity) it is in to the front, and:
If task currently has a child activity displayed, do nothing
If task doesn't have a child activity displayed (just root/main activity), process the passed data and update the UI
I have one task with a button, "Go to Event", when clicked it needs to open/start the Calendar task and highlight a specific event (but the Calendar will ignore that request if another activity is being displayed over the top of it).
Thanks to #qbix's comment, I found this answer:
Add Intent.FLAG_ACTIVITY_SINGLE_TOP -- which seems like it wouldn't be valid when combined with FLAG_ACTIVITY_NEW_TASK, but it works. It achieves all behaviors I listed above, and only fires onNewIntent if the activity is at the top of the stack, so I don't even have to worry about detecting that and ignoring.
More details here, even though this Q&A is related to PendingIntent and notifications:
Intent from notification does not have extras
I ran into a similar issue with the recent list in that there seem to a handful of conditions where an app will be completely destroyed but some other process will launch the app cold from the last activity that was being used. Since my app has a state that is built up over several designated activities I need to prevent this (i.e. null refs from onCreate()).
Without checking for state is all my onCreate() functions is there a way to just prevent this?
Also, other than the launcher, recents, the back button from other apps - are there more conditions where another thing can launch my app if I have not giving manifest permission to launch it explicitly with an intent?
Thanks!
If I understand this correctly, you have initializations in activity B that rely on activity A having passed them in. If an intent launches activity B first, without it being active, or being launched by A first, your activity will crash.
Easiest solution I can come up with is make your activity A (I assume your main activity) the broadcast listener for all the intents you wish to handle, and based off the intent action, dispatch to the appropriate child activities (B, C, whatever). That way activity A does all your initialization, and you can still launch into the appropriate activity to handle the original intent you wanted to.
Alternatively. If you detect your children activities are in an invalid state, you could put initialization into a parent activity that all your activities extend from. That way you should be able to initialize properly if the activity is a fresh launch. I'm not really a fan of this, I prefer making sure my activities are dependency injected with appropriate data.
In my application there is an activity started using the FLAG_ACTIVITY_SINGLE_TOP and FLAG_ACTIVITY_CLEAR_TOP flags because I want to make sure that only one instance of that activity is at the top of the stack and all activities on top of the old instance are closed. So far so good.
Next I wanted to test if the activity restores correctly after being created more than once and successively destroyed. I take care to manually set the intent using Activity.setIntent() when Activity.onNewIntent() is called so that the most recent intent is returned by Activity.getIntent(). In order to test that I activated the "Don't keep activities" option in the developer options, but the intent returned by Activity.getIntent() when the activity is re-created is the very first intent that created it and not the most recent one.
This happens on JB and ICS, I haven't tested it on older versions. Am I doing something wrong or did I misunderstand something in the docs?
If you kill your app while it is in the foreground, this is not the same as when Android kills your app (which it will only do when your app is in the background). If you kill and then restart the app, it is like starting it all over again from scratch. There is no "restore" going on here. If you add logging to onCreate() you should see that after you kill and restart your app, the Bundle that is passed to onCreate() is null.
Unfortunately it is pretty difficult to simulate what happens when Android kills your app.
EDIT: Added more stuff after OP's comment
Here's a concrete example for discussion purposes. First without the developer option "Don't keep activities":
ActivityA is the root activity
We start ActivityA
ActivityA.onCreate() is called
ActivityA now starts ActivityB
ActivityB.onCreate() is called (The activity stack now contains ActivityA->ActivityB)
ActivityB starts ActivityA with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
ActivityA.onNewIntent() gets called with the Intent containing FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
ActivityB.onDestroy() is called since the activity stack was cleared back to ActivityA
Now, let's do the exact same thing but enable the developer option "Don't keep activities" (I've highlighted in bold the stuff that is different from the previous scenario):
ActivityA is the root activity
We start ActivityA
ActivityA.onCreate() is called
ActivityA now starts ActivityB
ActivityB.onCreate() is called (The activity stack now contains ActivityA->ActivityB)
Because ActivityA has stopped, Android destroys it and calls ActivityA.onDestroy()
Note: The activity stack still contains ActivityA->ActivityB, even though there is no instance of ActivityA at the moment. Android remembers all the state
ActivityB starts ActivityA with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
Since Android has no instance of ActivityA to reactivate, it needs to create one, so it does and then...
ActivityA.onCreate() is called with the same Intent that it was called with when the original instance of ActivityA was created (ie: LAUNCH intent with no flags and no extras)
ActivityA.onNewIntent() gets called with the Intent containing FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
ActivityB.onDestroy() is called since the activity stack was cleared back to ActivityA
The important thing to note here is that Android always calls onCreate() whenever it creates an activity instance. Think of it like the constructor of an Activity. If Android has to recreate an instance of an Activity because the process was killed or the activity was destroyed, then it will instantiate a new object, then call onCreate() and then (if necessary) it will call onNewIntent().
When you call setIntent() this doesn't actually change the Intent that Android saves and restores. That only changes the in-memory Intent that will be returned from a call to getIntent().
I hope this is clearer now. If not, please let me know.
Not sure if you've found a solution to this or not, but overriding the onNewIntent(Intent theNewIntent) method for the target activity & calling setIntent(theNewIntent) solved it for me.
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
/*
* This overrides the original intent.
*/
setIntent(intent);
}
Suppose the current task stack of my app P contains activities: A B. A started B with some intent i.
A is defined with android:alwaysRetainTaskState flag.
Then user switched to other app, and after a while the process of P is killed by OS.
Then user started P from home screen. Since A has android:alwaysRetainTaskState flag, the stack will be restored to A B, and B is visible. My understanding is that, only B.onCreate() will be called and A.onCreate() will not be called. Am I right?
Besides, at the moment, does B still have the intent i? That is, when B calls getIntent(), will getIntent() returns null or an intent object, i?
Thanks!
My understanding is that, only B.onCreate() will be called and A.onCreate() will not be called. Am I right?
Yes, you are.
Besides, at the moment, does B still have the intent i? That is, when B calls getIntent(), will getIntent() returns null or an intent object, i?
getIntent() will work as always - it will return Intent object equal to one you have passed from activity A (not the same instance, but deserialized one)
This answer comes from my experiments (with android 2.3), but it fits neatly into Android's ideology of independent activities (IMHO it's the only reason why Intent can contain only serializable data even when it's used within one application), so I believe it's true for all android versions.
And "alwaysRetainTaskState" isn't even necessary - system will lead you to activity B even with default "false" value if you return to you task quickly (according to http://developer.android.com/guide/topics/manifest/activity-element.html#always, system will only reset task state "if the user hasn't visited the task for a certain amount of time, such as 30 minutes").