Familiar scenario: I have a Main activity that launches a Game activity when a button is pressed. If the user presses HOME, and then launches my application again, it should be presented with the Game activity, which is what he was doing last when using the application.
However, what happens instead is he gets the Main activity again. I have the feeling that Android is creating another instance of MainActivity and adding it to the stack for that application, instead of just picking whatever was on the top, because if I press BACK after relaunching the app, I get to the Game activity! And the Main.onCreate method is called every time, instead of calling GameActivity.onResume.
My AndroidManifest.xml
is pretty much 'bare bones':
<activity android:name="MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="GameActivity" android:label="@string/app_name">
</activity>
As you can see, nothing too fancy.
And this is how the new activity is launched, very simple too:
Intent intent = new Intent(this, GameActivity.class);
startActivity(intent);
In theory this should work in Android just "out of the box", as the answer to a very similar question says: Maintaining standard application Activity back stack state in Android (using singleTask launch mode), but it isn't.
I've been reading and re-reading the documentation on Activity and Task and Stacks and browsing all related answers in SO but I can't understand why such a simple setup is not quite working as expected.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity was brought to front and not created,
// Thus finishing this will get us to the last viewed activity
finish();
return;
}
// Regular activity creation code...
}
Oh, I think I've found the answer.
Because I was launching the app by using IntelliJ, it seems it launches the application in a different way than a user, clicking a home screen widget, would launch. It's explained in the answer to another SO question:
This is due to the intents being used to start the app being different. Eclipse starts an app using an intent with no action and no category. The Launcher starts an app using an intent with android.intent.action.MAIN action and android.intent.category.LAUNCHER category. The installer starts an app with the android.intent.action.MAIN action and no category.
Ref: App always starts fresh from root activity instead of resuming background state (Known Bug)
So I've manually killed the application in the phone, and relaunched it again from the home screen widget. Then opened the GameActivity, and pressed HOME. Now, when relaunching it the GameActivity is still visible and keeping its UI state as it was when I left it.
And I guess that the reason why a new instance of the activity was created when pressing the shortcut before was due to a different Intent being used for starting the activity.
The simplest solution is to write a preference out during onPause or serialize your state out to a persistent file and then read this file during onResume in your main entry point Activity. This activity would rebuild the application state and re-launch the correct activity.
You're seeing this because you app may be killed by the OS on exit. You never know for sure so if you want true persistence of usage, ie go back to the last activity no matter what, you need to write your state to a persistent store.
I would highly recommend using isTaskRoot to check whether the activity should be finished instead of the FLAG_ACTIVITY_BROUGHT_TO_FRONT flag.
I liked Sachin's answer, but sometimes it gave me false-positives and I would finish the activity when I shouldn't. The reliable way I found to reproduce such a false positive is:
- Launch the app from homescreen
- Press home
- Launched the app from home screen again
- Press "back" to return to home screen
- Returned to the app from "recent apps"
I started looking for ways to inspect my task's activity backstack and found answers using ActivityManager. This seemed like it could work, but this answer pointed out the existence of isTaskRoot, which is a simple, elegant way of detecting this which doesn't give false positives, doesn't require a special permission and doesn't use methods that docuemtation says are intended for debugging and task management apps and which are unsupported for this type of use.
I understand where you are coming from but I think the os doesn't want to make any assumptions. Plus there is the issue where your app has been reclaimed because memory is need for something else. In that cause it would be starting over from your MainActivity.
Depending on how complicated your game is your can either save state in SharedPreferences or a SqlLite database in the onPause() method of your activity. Then onResume() you can restore the user to the last state, which may include "re-starting" your GameActivity.
Hope this helps!
Edit: And what I am saying is that's our responsibility as developers to show / guarantee to the user that the correct activity is displayed when coming back to the application. You application's memory always has the potential of being reclaimed and then android is going to re-start the activity that you have labeled with MAIN and LAUNCHER intents. By saving the state (the activity id) you can redirect them to that activity from main...
I solve the problem using the following activity stack in my application:
LaunchActivity -> MainActivtiy -> SomeSubActivtiy
LaunchActivtiy only verifies some parameters and launches MainActivtiy.
It turned out, that MainActivity must be launched with
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
That combination of flags on MainActivity gives desired behaviour.