Imagine that you launch a singleTop
activity using intent A and then launch it again using intent B. As a result, activity’s onNewIntent
method is called and inside this method you call setIntent
to store the new intent. After that, Android decides to relaunch the activity. Now we have the question: which intent will you get after Android relaunches the activity? The original one(A) or the latest one(B)?
The answer is you may get either one, and it depends on why Android relaunches the activity. In some situations, Android will relaunch your activity because Android kills it(indeed the process) to reclaim memory and then you navigate back to it. In this case, you will get the original intent instead of the latest one. Android will also relaunch your activity(it’s the default behavior) due to the configuration change and you’ll get the latest intent. Now let’s discuss these two cases separately and see why we get different intents in these two cases.
Killed Process
In Android, activities, taskes and processes are managed by ActivityManagerService
and each activity has a corresponding ActivityRecord
. As we can see, ActivityRecord
stores lots of information related to the activity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
When you launch a singleTop
activity using intent A, some process(maybe the Launcher) sends a request to ActivityManagerService
(through Binder RPC) and ActivityManagerService
creates an ActivityRecord
. Now the intent variable of the new ActivityRecord
object is A. Then ActivityManagerService
may create the application process(if not exists) and asks the application process(through Binder RPC) to launch the specified Activity
using intent A. In the application process, ActivityThread
receives and handles the request. It creates an Activity
object and calls lifecycle methods.
Then you launch the activity again using intent B and ActivityManagerService
receives the request. It finds that there’s already an ActivityRecord
on the top of the stack, so it decides to deliver the new intent B to this existing activity. Here we have to notice that the intent variable of the ActivityRecord
object is still A and it’s immutable(final). After ActivityManagerService
sends the new intent request, ActivityThread
receives it and calls activity’s onNewIntent
method.
At some point, Android decides to kill the application process due to low memory. After that, you navigate back to this activity and ActivityManagerService
has to relaunch it using the information stored in ActivityRecord
. As you can see, ActivityRecord
only stores the original inten(A) and the latest one(B) is lost. Why? Becuase the latest intent is only stored in the Activity
and now the activity(process) is killed! So, in this case, you’ll get the original intent after Android relaunches the activity.
Configuration Change
After you launch the activity again using intent B, you decide to rotate the screen. Now we have the configuration change. When ActivityManagerService
detects configuration changes, it relaunches the current activity by sending a request to ActivityThread
. ActivityThread
handles the request and relaunches the activity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
As we can see, ActivityThread
finds the activity and stores its latest intent in a variable. Then it destroys the activity and relaunches the activity using the latest intent. Because the application process is not killed in this case, the latest intent is not lost and we can get it after Android relaunches the activity.
Conclusion
Because you don’t know which intent you will get after Android relaunches the activity, you should use savedInstanceState
to restore activity’s state. In fact, ActivityRecord
stores activity’s savedInstanceState
in its icicle variable, so it won’t be lost!
Reference
https://groups.google.com/forum/#!topic/android-developers/vrLdM5mKeoY
The source code of ActivityThread, ActivityManagerService, ActivityStackSupervisor, ActivityStack, ActivityRecord, TaskRecord and ProcessRecord.