Неожиданное поведение с REORDER_TO_FRONT

Действие A запускает действие B без флагов. Стек теперь A-B с B сверху. B начинает активность A с FLAG_ACTIVITY_REORDER_TO_FRONT (единственным флагом). Я ожидаю, что теперь стек будет B-A. Однако при нажатии кнопки "Назад" в этот момент он возвращается к главному экрану. Здесь я ожидаю, что активность B будет перенесена на фронт. При повторном нажатии на значок запуска приложение открывается с помощью B в качестве текущей активности и ничего в стеке.

Launchmode является стандартным (по умолчанию) в манифесте.

Является ли это ожидаемым поведением, и я просто не понимаю его правильно?

EDIT: Я создал тестовый проект без каких-либо смешающих факторов и все еще вижу такое же поведение. Я просто этого не понимаю, похоже, это не соответствует документации.

РЕДАКТИРОВАТЬ: Мне кажется, что это поведение является ошибкой в ​​структуре, см. мой комментарий к ответу ниже. Мне нужно обходное решение.


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);
}

}

открытый класс SecondActivity расширяет активность {   @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);

}

} код >

манифеста:


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

Ответы

Ответ 2

Прежде всего, давайте начнем с того, что вы правы!

Но если моя логика правильная, то что произойдет, когда вы REORDER на ваш основной Activity (Launcher Activity), Intent установлен так, что обратное нажатие вернет вас к Launcher.

В качестве эксперимента попробуйте добавить Activity C и попробуйте REORDER B спереди от C. Это:
A- > B- > C... A- > C- > B

Если порядок очень важен для вас, вам может потребоваться переопределить метод Activity.onNewIntent().

@Override
protected void onNewIntent(Intent intent) {
}

Ответ 3

Я нашел простой способ обхода этой ошибки.

Переопределить onNewIntent и завершить функции любого из них могут переупорядочиваться на переднюю активность, так как следующее будет делать трюк, просто не полностью экзамен, если вы обнаружите какую-либо проблему для этого обходного пути, пожалуйста, свяжитесь со мной по [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 finished.
        ActivityManager tasksManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        tasksManager.moveTaskToFront(getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION);
    }
}

Ответ 4

Я не мог найти эту проблему вообще. Вот проект, который я создал.

Я только что переопределил метод Finish(), чтобы проверить, какая активность закрыта.

Ответ 5

Я столкнулся с этой ошибкой и решил, что мне, возможно, придется обойти это. И, по крайней мере, в моем случае это кажется возможным, предоставляя пользовательскую реализацию back stack для версии 4.4.2. Это ни в коем случае не симпатично и может не работать во всех ситуациях, но это спасло меня и мою навигацию на основе DrawerLayout.

Во-первых, у меня есть NavigationDrawerActivity как класс, который распространяется на все остальные действия. Там у меня есть статический Stack для вызываемых классов, а также массив классов, к которым можно получить доступ из навигационного ящика. Метод addClassToStack является общедоступным, так что другие средства навигации, кроме ящика, также могут использовать стек. Обратите внимание, что класс, который должен быть добавлен в стек, сначала удаляется (если он существует), так что мы получаем ту же функциональность, что и нормальный флаг переупорядочения. Я окружил взломанный код проверками версий, чтобы хак использовался только при необходимости.

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);
        }
    }
    ...

Далее - класс слушателя для навигационного ящика. Кнопки на моем ящике находятся в ListView, поэтому каждый раз, когда пользователь хочет куда-то пойти, здесь будет вызываться onItemClick. Единственная "хакерская" вещь - вызов addClassToStack для добавления нового действия в качестве вершины заднего стека.

    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);
        }
    }

Последняя часть обходного пути заключается в переопределении метода onKeyDown, и в случае нажатия кнопки "Назад" на Android 4.4.2 (и мы вышли за рамки первого действия в нашей навигации), активность, которую нужно открыть, выбирается из нашего пользовательского 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;
        }
    }
}

Единственное, что осталось (и не показано здесь), это добавить начальную активность (ту, которая открывается при запуске приложения) в задний стек отдельно в соответствующем месте. В моем приложении у меня есть отдельный экран запуска, который недоступен через ящик навигации, поэтому я могу вызвать NavigationDrawerActivity.addClassToStack(StartScreenActivity.class) из этого действия onCreate. Для других структур вы можете сделать что-то другое, чтобы убедиться, что начальная активность добавляется в стек только один раз в качестве самого первого элемента.

Теперь это хак и тот, который я еще не тестировал очень тщательно, но, похоже, работает на Nexus 4 (4.4.2) и на Nexus S (застрял на 4.1). Поэтому, если вы попытаетесь сделать что-то подобное, и это не сработает, не сердитесь, но дайте мне знать.:)

Ответ 6

Я вижу, что эта проблема происходит на ОС S7 7.0, где, когда активность задана с помощью Flag Intent.FLAG_ACTIVITY_REORDER_TO_FRONT, и это действие завершается, Пользователь переводится на главный экран устройства.

Исправьте, я вложил для этого - удалил флаг Intent.FLAG_ACTIVITY_REORDER_TO_FRONT и добавил noHistory = true

Таким образом, новый экземпляр активности попадает в стек, а поскольку noHistory истинна, старый экземпляр, который уже является частью стеков, уничтожается.