Сценарий: приложение уже открыто, повторное нажатие push-уведомления создаёт второй экран заказа. Как проверить, что причина связана с режимом запуска Activity?
<activity
android:name=".OrderActivity"
android:launchMode="standard" />
Нужно проверить, создаётся ли новый экземпляр Activity при повторном запуске, либо существующий получает новый Intent. Для этого следует зафиксировать идентификатор экземпляра, последовательность onCreate/onNewIntent и состояние стека задач Android.
При launchMode="standard" каждый запуск обычно создаёт новый экземпляр OrderActivity. Если при повторном нажатии появляется новый идентификатор экземпляра и снова вызывается onCreate, причина находится в политике запуска, а не обязательно в повторном выполнении бизнес-операции.
Android изначально строит навигацию вокруг Activity и стека задач. Такой подход позволяет сохранять историю экранов, возвращаться к предыдущим состояниям кнопкой «Назад» и запускать один экран из разных источников: другого приложения, уведомления или системного компонента.
Режим standard является базовым, потому что независимые запуски должны поддерживать независимые экземпляры экранов. Для сценариев, где повторный запуск должен переиспользовать верхний экран, Android предоставляет другие режимы и флаги намерения.
Дублирование экрана заказа может привести к нескольким проблемам: пользователь нажимает «Назад» несколько раз, видит устаревшее состояние, повторно выполняет действие или получает несколько запросов загрузки. Внешне это может выглядеть как дефект push-уведомлений или навигации, хотя корень находится в формировании стека Activity.
Важно отделить создание второго экрана от повторной обработки операции. Даже при одном экземпляре Activity обработчик нового Intent способен дважды запустить сетевой запрос, если он не учитывает идентификатор заказа и повтор события.
Сначала нужно воспроизвести сценарий на чистом состоянии: открыть приложение, открыть заказ, нажать одно и то же уведомление повторно. В журнале тестовой сборки следует записывать идентификатор экземпляра Activity, момент вызова onCreate, onStart, onNewIntent и значение orderId.
Интерпретация должна быть такой:
onCreate — создан дополнительный экземпляр;onNewIntent — существующий верхний экземпляр получил новое намерение;В приведённой конфигурации standard повторный запуск допускает добавление новой OrderActivity в стек. singleTop переиспользует экземпляр только тогда, когда он уже находится наверху стека; в этом случае новое намерение поступает через onNewIntent. Если экран находится ниже вершины, singleTop не предотвращает создание нового экземпляра.
singleTask или комбинация флагов вроде CLEAR_TOP меняют поведение всего стека, поэтому применять их только ради устранения визуального дубля рискованно: можно удалить промежуточные экраны и изменить ожидаемую навигацию. Обычно безопаснее явно определить требование: нужен один экран заказа или отдельный экземпляр для каждого перехода, затем отдельно сделать обработку уведомления идемпотентной.
Минимальная диагностическая логика должна выглядеть так:
Тестировать нужно не только холодный запуск, но и приложение на переднем плане, приложение в фоне, наличие другого экрана поверх заказа, повторное нажатие уведомления и быстрые последовательные нажатия. Такой набор показывает, зависит ли результат от позиции Activity в стеке.
В приложении повторное нажатие уведомления открывало два одинаковых заказа. Первый вариант исправления — всегда использовать singleTask: он мог убрать дубль, но неожиданно очищал промежуточные экраны из стека, поэтому кнопка «Назад» стала возвращать пользователя не туда.
Второй вариант — заменить режим на singleTop и обрабатывать onNewIntent. Он сохранил обычную навигацию, но не решал случай, когда поверх заказа уже находился другой экран. Кроме того, обработчик уведомления всё ещё мог повторно запускать загрузку.
Выбранное решение состояло из двух частей: продукт явно определил правило «один экземпляр заказа на вершине текущей навигации», а обработчик нового намерения сравнивал orderId и не повторял уже выполняемую операцию. Тесты с журналом экземпляров подтвердили отсутствие лишнего Activity, а тесты быстрых повторных нажатий — отсутствие дублирующих запросов.
Нет. Два разных экземпляра могут визуально показывать один и тот же экран, а один экземпляр может повторно выполнить действие. Нужно сопоставлять идентификатор экземпляра, callbacks жизненного цикла, содержимое нового Intent и сетевые запросы.
singleTop не является универсальным решением?Он переиспользует только Activity, находящуюся на вершине стека. Если поверх заказа открыт другой экран, новый запуск заказа обычно создаст дополнительный экземпляр. Поэтому ожидаемый результат нужно проверять для каждой позиции экрана в стеке, а не только для состояния «заказ уже открыт последним».
onCreate, но не onNewIntent?При переиспользовании Activity новое уведомление может прийти через onNewIntent, поэтому код, читающий параметры только в onCreate, оставит пользователю старый заказ. Тест должен открыть один заказ, затем передать уведомление с другим orderId и убедиться, что экран обновился корректно без создания лишнего экземпляра.