Режим запуска Android "Single Top" и метод onNewIntent

Я прочитал в документации по Android, что, установив для свойства Activity в свойстве launchMode значение singleTop ИЛИ, добавив FLAG_ACTIVITY_SINGLE_TOP флаг к моему намерению, что зовет startActivity(intent) будет повторно использовать один экземпляр Activity и даст мне намерение в onNewIntent Перезвоните. Я сделал обе эти вещи, и onNewIntent никогда не стреляет и onCreate срабатывает каждый раз. Документы также говорят, что this.getIntent() возвращает намерение, которое было сначала передано Деятельности, когда это было сначала создано. В onCreate Я зову getIntent и я получаю новый каждый раз (я создаю объект намерения в другом действии и добавляю дополнительный к нему... этот дополнительный должен быть одинаковым каждый раз, если он возвращал мне тот же объект намерения). Все это заставляет меня поверить, что моя деятельность не ведет себя как "единственная вершина", и я не понимаю, почему.

Чтобы добавить некоторый фон в случае, если я просто пропускаю обязательный шаг, вот мое объявление Activity в манифесте и код, который я использую для запуска действия. Сама деятельность не делает ничего заслуживающего упоминания в отношении этого:

в AndroidManifest.xml:

    <activity
        android:name=".ArtistActivity"
        android:label="Artist"
        android:launchMode="singleTop">
    </activity>     

в моем призвании

        Intent i = new Intent();
        i.putExtra(EXTRA_KEY_ARTIST, id);
        i.setClass(this, ArtistActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(i);

3 ответа

Решение

Вы проверили, если onDestroy() был также назван? Наверное, поэтому onCreate() вызывается каждый раз вместо onNewIntent(), который будет вызываться только в том случае, если действие уже существует.

Например, если вы оставите свою деятельность с помощью кнопки BACK, она будет уничтожена по умолчанию. Но если вы поднимитесь выше в стеке активности к другим занятиям и оттуда позвоните ArtistActivity.class опять пропущу onCreate() и идти прямо к onNewIntent()потому что действие уже было создано, и так как вы определили его как singleTop Android не будет создавать новый экземпляр этого, но возьмите тот, который уже валяется.

Что я делаю, чтобы увидеть, что происходит, я реализую фиктивные функции для всех различных состояний каждого действия, поэтому теперь я всегда понимаю, что происходит:

@Override
public void onDestroy() {
    Log.i(TAG, "onDestroy()");
    super.onDestroy();
}

То же самое для onRestart(), onStart(), onResume(), onPause(), onDestroy()

Если вышеописанное (кнопка BACK) не было вашей проблемой, реализация этих макетов, по крайней мере, поможет вам отладить их немного лучше.

Принятый ответ не совсем корректен. Если onDestroy() вызывался ранее, то да, onCreate() всегда вызывался бы. Однако это утверждение неверно: "Если вы поднимитесь выше в стеке действий к другим действиям и оттуда снова вызовите свой ArtistActivity.class, он пропустит onCreate () и перейдет непосредственно к onNewIntent()"

Раздел "singleTop" на http://developer.android.com/guide/components/tasks-and-back-stack.html объясняет, как это работает (внимание к тексту, выделенному жирным шрифтом ниже; я также доказал это с помощью своего собственного отладка):

"Например, предположим, что задний стек задачи состоит из корневого действия A с действиями B, C и D вверху (стек ABCD; D вверху). Достигается намерение для действия типа D. Если D имеет стандартный режим запуска по умолчанию, запускается новый экземпляр класса, и стек становится ABCDD. Однако, если режим запуска D- "singleTop", существующий экземпляр D получает намерение через onNewIntent(), потому что он находится наверху стека - стек остается ABCD. Однако, если поступает намерение для действия типа B, то в стек добавляется новый экземпляр B, даже если его режим запуска "singleTop"."

Другими словами, запуск действия через SINGLE_TOP будет повторно использовать существующее действие, только если оно уже находится на вершине стека. Это не будет работать, если другое действие в той же задаче находится вверху (например, действие, которое выполняет startActivity(SINGLE_TOP)); вместо этого будет создан новый экземпляр.

Вот два способа исправить это, чтобы получить желаемое поведение SINGLE_TOP, основной целью которого является повторное использование существующего действия вместо создания нового...

Первый способ (как описано в разделе комментариев принятого ответа): Вы можете добавить режим запуска "singleTask" в свою активность. Это вызовет onNewIntent(), потому что singleTask означает, что в данной задаче может быть только ОДИН экземпляр конкретного действия. Это несколько хакерское решение, потому что, если вашему приложению требуется несколько экземпляров этого действия в конкретной ситуации (как я это делаю для своего проекта), вы облажались.

Второй способ (лучше): вместо FLAG_ACTIVITY_SINGLE_TOP используйте FLAG_ACTIVITY_REORDER_TO_FRONT. Это позволит повторно использовать существующий экземпляр действия, переместив его в верхнюю часть стека (onNewIntent() будет вызываться, как и ожидалось).

Основная цель FLAG_ACTIVITY_SINGLE_TOP - предотвратить создание нескольких экземпляров Activity. Например, когда эта деятельность может быть запущена через намерение, которое приходит извне основной задачи вашего приложения. Для внутреннего переключения между действиями в моем приложении я обнаружил, что вместо этого обычно используется FLAG_ACTIVITY_REORDER_TO_FRONT.

Установите этот флаг для вашего намерения:

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
Другие вопросы по тегам