Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puzzling behavior with REORDER_TO_FRONT

Tags:

android

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>

like image 999
Steve M Avatar asked Dec 20 '13 02:12

Steve M


5 Answers

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

it had been confirmed by google as a bug on 4.4.2

like image 186
ljian Avatar answered Sep 28 '22 14:09

ljian


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) {
}
like image 25
Assaf Gamliel Avatar answered Sep 28 '22 13:09

Assaf Gamliel


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 [email protected]

@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);
    }
}
like image 20
Ricotta Avatar answered Sep 28 '22 14:09

Ricotta


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.

like image 40
Tabrej Khan Avatar answered Sep 28 '22 13:09

Tabrej Khan


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

like image 40
J.Nieminen Avatar answered Sep 28 '22 13:09

J.Nieminen