How to return to the latest launched activity when re-launching application after pressing HOME? [duplicate]
Asked Answered
D

5

25

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.

Derbyshire answered 13/6, 2011 at 22:35 Comment(1)
D
18

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.

Derbyshire answered 14/6, 2011 at 8:22 Comment(4)
Looks like you found your own way to explain the same answer that 2 other people were trying to tell you... ;0)Stepfather
@Stepfather it doesn't look like that to meMaclay
Lifesaver. This was a huge pain on my back for testing.Beneficent
Nice find! Save me some timeMarxism
E
2

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.

Enclave answered 13/6, 2011 at 23:1 Comment(5)
You're talking about data persistence whereas I'm talking about "temporary/transient activity stack" persistence, which is a feature that the system does provide, way before the OS can kill the task. Sorry if I wasn't clear enough but I think you're answering something else.Derbyshire
@sole: I think you may be misunderstanding a core concept of Android development. Or I am missing something in your question. The activity stack is managed by the OS and tied to your process. If you leave you app by going home or using back the OS has every right to kill your app which isn't so temporary.Enclave
The app hasn't been killed yet, actually apps take quite a long time to be killed. I'm talking about pressing HOME and pretty much instantly coming back (such as when interrupted by a phone call). The application's activity stack is preserved by the OS for those cases :-)Derbyshire
@sole: are you sure? App killing takes less than 50 ms for my game and happen a lot because most phones are memory starved and it takes a bit of memory so the OS kills it. By default your app will be resumed back to the last activity, if not then you are doing something "odd" or your app is being killed.Enclave
Yes, please see my own answer. The app wasn't killed. Instead, another instance of MainActivity was created, due to the first one having been created via my IDE.Derbyshire
P
2

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:

  1. Launch the app from homescreen
  2. Press home
  3. Launched the app from home screen again
  4. Press "back" to return to home screen
  5. 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.

Profusive answered 27/5, 2013 at 22:12 Comment(0)
S
1

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...

Stepfather answered 13/6, 2011 at 23:0 Comment(1)
It's not a problem of saving internal state. It's a problem of the proper Activity not being shown when coming back to the application.Derbyshire
K
1

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.

Kerin answered 3/12, 2011 at 10:30 Comment(1)
This looks like a terrible solution but... for a lack of finding flags to set in the manifest, I daresay you're right. :'(Grievance

© 2022 - 2024 — McMap. All rights reserved.