26

Activity A starts activity B with no flags. The stack is now A-B with B on top. B starts activity A with FLAG_ACTIVITY_REORDER_TO_FRONT (the only flag). I would expect the stack to now be B-A. However, when the back button is pressed at this point it returns to the home screen. Here I would expect Activity B to be brought to the front. Upon clicking on the launcher icon again, the app opens with B as the running activity and nothing in the stack.

Launchmode is standard (default) in the manifest.

Is this the expected behavior and I'm just not understanding it properly?

EDIT: I have created a test project with no confounding factors and still see the same behavior. I just don't understand it, it doesn't seem to be per the documentation.

EDIT: To me this behavior seems to be a BUG in the framework, see my comment on answer below. I need a workaround.

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}
public void onClickBtn(View view)
{
    Intent flowIntent = new Intent(this, SecondActivity.class);
    startActivity(flowIntent);
}

}

public class SecondActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); }

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}
public void onClickBtn(View view)
{
    Intent flowIntent = new Intent(this, MainActivity.class);
    flowIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    startActivity(flowIntent);

}

}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android&quot;
    package="com.example.tester"
    android:versionCode="1"
    android:versionName="1.0" >
 <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.tester.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="com.example.tester.SecondActivity" />
    </application>
</manifest>

Steve M
  • 9,296
  • 11
  • 49
  • 98

6 Answers6

22

http://code.google.com/p/android/issues/detail?id=63570#c2

it had been confirmed by google as a bug on 4.4.2

ljian
  • 976
  • 7
  • 8
9

First of all, lets start by saying that you're right!

But if my logic is correct, what happens when you REORDER to your main Activity (the Launcher Activity), the Intent is set so that the back press will return you to the Launcher.

As an experiment, try to add Activity C and try to REORDER B to the front from C. That is:
A->B->C ... A->C->B

If the order is very important for you, you might need to override Activity.onNewIntent() method.

@Override
protected void onNewIntent(Intent intent) {
}
Assaf Gamliel
  • 11,935
  • 5
  • 41
  • 56
  • setIntent(intent) doesn't solve the problem, the behavior is still the same. – Steve M Dec 23 '13 at 23:28
  • "As an experiment, try to add Activity C and try to REORDER B to the front from C. That is: A->B->C ... A->C->B" This works as expected. Also if you make Activity C REORDER A to front (hence B-C-A), pressing the back button from Activity A at this point will go back to C as expected and NOT to the launcher. I can only surmise that the behavior in the two Activity example I originally gave is a BUG in the framework! – Steve M Dec 24 '13 at 15:49
8

I find a simple workaround for this bug.

Override onNewIntent and finish functions of any may reorder to front activity as following will make a trick, just not so fully test, if you find any problem for this workaround please contact me by ricotta.zhang@myriadgroup.com

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if ((intent.getFlags() | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) > 0) {
        mIsRestoredToTop  = true;
    }
}

@Override
public void finish() {
    super.finish();
    if (android.os.Build.VERSION.SDK_INT >= 19 && !isTaskRoot() && mIsRestoredToTop) {
        // 4.4.2 platform issues for FLAG_ACTIVITY_REORDER_TO_FRONT,
        // reordered activity back press will go to home unexpectly,
        // Workaround: move reordered activity current task to front when it's finished.
        ActivityManager tasksManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        tasksManager.moveTaskToFront(getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
    }
}
Ricotta
  • 81
  • 1
  • 1
2

I couldn't find this issue at all. Here is the project I have created.

I have just overrided onFinish() method to check which Activity got closed.

Tabrej Khan
  • 894
  • 6
  • 15
  • What version? I've only tested on 4.4.2 so far. I guess it could be a version specific occurrence. – Steve M Dec 25 '13 at 19:22
  • Check the sample on your device or emulator. I checked with android-19 i.e. Android 4.4 Kitkat. – Tabrej Khan Dec 25 '13 at 19:33
  • Tried your project, clearly a bug in framework with 4.4.2. Both my 4.4.2 devices and 4.4.2 emulator have the wrong behavior. On 4.3 and 4.1.2 emulator the behavior is correct. Did you update your SDK to 4.4.2? I'm sure you will see it too. Please update your answer to include that this is apparently version specific bug and I will award bounty. I am going to make bug report. – Steve M Dec 25 '13 at 22:27
  • I checked on Android 4.2.2 device. Took update of latest Android 4.4.2 version but need to check on emulator with 4.4.2 – Tabrej Khan Dec 26 '13 at 06:53
1

I ran into this bug as well and decided that I might need to work around it. And at least in my case, it seems to be possible by providing a custom back stack implementation for the version 4.4.2. This is by no means pretty, and might not work in all situations, but it did save me and my DrawerLayout based navigation.

First, I have a NavigationDrawerActivity as a class that all other activities extend. In there I have a static Stack for the classes that are called as well as an array of classes that can be accessed from the navigation drawer. The addClassToStack method is public so that other means of navigation besides the drawer can also use the stack. Note how the class to be added to the stack is first removed (if it exists) so that we get the same functionality as normally provided by the reorder-to-front flag. I've surrounded the hacky code with version checks so that the hack is only used when necessary.

public class NavigationDrawerActivity extends Activity {
    ...
    private static Stack<Class<?>> classes = new Stack<Class<?>>();
    private Class<?>[] activity_classes;
    ...
    public static void addClassToStack(Class<?> to_add) {
        if (android.os.Build.VERSION.RELEASE.equals("4.4.2")) {
            classes.remove(to_add);
            classes.push(to_add);
        }
    }
    ...

Next up is the listener class for the navigation drawer. The buttons on my drawer are in a ListView so each time the user wants to go somewhere, the onItemClick here will be called. The only "hacky" thing here is the call to the addClassToStack to add the new activity as the top of the back stack.

    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        private Intent i;
        @Override
        public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
            i = new Intent(NavDrawerActivity.this, activity_classes[pos]);
            addClassToStack(activity_classes[pos]);
            i.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
            nav_drawer.closeDrawers();
            startActivity(i);
        }
    }

Last part of the workaround is to override the onKeyDown method and, in case the back button is pressed on Android 4.4.2 (and we are beyond the first activity in our navigation), the activity to be opened is fetched from our custom back stack and called directly. Note that the topmost item in the stack is always the current activity and thus we need to get rid of that and use the second one as a target. Also note how I'm clearing the official task history whenever returning to the first activity. This was required because otherwise, after pressing back once more and returning to the home screen, the next time the app was accessed it went straight to the top of the official back stack.

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if (keyCode == KeyEvent.KEYCODE_BACK &&
            android.os.Build.VERSION.RELEASE.equals("4.4.2") &&
            classes.size() > 1) {
            classes.pop();
            Intent prev = new Intent(this, classes.peek());
            if (classes.size() == 1) {
                prev.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            } else {
                prev.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
            }
            startActivity(prev);
            handled = true;
        }
        if (!handled) {
            return super.onKeyDown(keyCode, event);
        } else {
            return true;
        }
    }
}

The only thing left (and not shown here) is to add the initial activity (the one that gets opened when the app starts) into the back stack separately in an appropriate place. In my app, I have a separate start screen that isn't accessible via the navigation drawer, so I can call NavigationDrawerActivity.addClassToStack(StartScreenActivity.class) from that activity's onCreate. For other structures, you might do something different to make sure that the initial activity is added to the stack only once as the very first item.

Now, this is a hack and one that I haven't tested very thoroughly yet, but it seems to work on the Nexus 4 (4.4.2) and on a Nexus S (stuck on 4.1) as well. So if you try to do something like this and it doesn't work, don't be angry but let me know instead. :)

J.Nieminen
  • 566
  • 5
  • 12
0

I am seeing that this issue is happening on S7 7.0 OS where when activity is set with Flag Intent.FLAG_ACTIVITY_REORDER_TO_FRONT and this activity finishes itself, User is taken to home screen of device.

Fix I put in for this is - removed the flag Intent.FLAG_ACTIVITY_REORDER_TO_FRONT and added noHistory=true

This way new instance of activity gets pushed to stack and since noHistory is true, old instance that is already part of stacks get destroyed.