Приложение перезапускается, а не возобновляется

195

Надеюсь, кто-нибудь может помочь мне понять, если не решение, хотя бы объяснение поведения.

Эта проблема:

На некоторых устройствах нажатие значка запуска приводит к возобновлению текущей задачи, на других - к запуску первоначального намерения запуска (фактически перезапуска приложения). Почему это происходит?

Деталь:

Когда вы нажимаете «Значок запуска», приложение запускается нормально - то есть, я полагаю, запускается Intent с именем вашего первого Activityдействия android.intent.action.MAINи категорией android.intent.category.LAUNCHER. Однако это не всегда так:

На большинстве устройств, если вы нажмете значок запуска после того, как приложение уже запущено, текущее действие в этом процессе возобновится ( НЕ начальное Activity). Он возобновляется так же, как если бы вы выбрали его из «Недавних задач» в меню ОС. Это поведение, которое я хочу на всех устройствах.

Однако на выбранных других устройствах происходит другое поведение:

  • На Motorola Xoom, когда вы нажимаете значок запуска, приложение всегда запускает начальный запуск Activityнезависимо от того, что в данный момент работает. Я предполагаю, что значки запуска всегда запускают намерение «ЗАПУСК».

  • На вкладке Samsung 2, когда вы нажимаете значок программы запуска, если вы только что установили приложение, оно всегда запускает исходное Activity(то же, что и Xoom) - однако после перезагрузки устройства после установки значок программы запуска вместо возобновить приложение. Я предполагаю, что эти устройства добавляют «установленные приложения» в таблицу поиска при запуске устройства, что позволяет значкам панели запуска правильно возобновлять выполнение задач?

Я прочитал много ответа , что звук похож на мою проблему , а просто добавление android:alwaysRetainTaskState="true"или использования launchMode="singleTop"в Activityне ответ.

Редактировать:

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

Graeme
источник
1
Это может показаться тривиальным вопросом, но вы установили значение «Не сохранять активность» в своих вариантах разработки для Xoom?
Эндрю Шустер
Нет (я хочу! :)) - я зарегистрировал жизненный цикл каждого действия и действий в фоновом режиме как все еще доступные (они остановлены - не уничтожены). Кажется, ОС вызывает finish()их в тех случаях, когда она начинает сначала, Activityа не возобновляет их.
Грэм
1
Если вы нажали кнопку «Домой», а затем щелкнули значок запуска, поведение возобновления по умолчанию для Android, как вы, вероятно, знаете. Однако, если вы нажмете кнопку «Назад», чтобы вернуться на домашний экран, большинство телефонов закроют () приложение. Возможно ли, какой бы метод вы не использовали для выхода из приложения на разных устройствах? Не могли бы вы выйти из onKeyUpEvent, чтобы убедиться, что некоторые странно не обрабатывают жесткие / мягкие клавиши?
Ник Кардосо
2
Нет - я уверен в проблеме, как указано выше. Использование home, чтобы поместить приложение в фоновый режим (не обратно, что вы правы, завершит () действие). В Xoom возможно возобновить работу приложения из списка задач (но не из панели запуска), поэтому backstack определенно не был уничтожен.
Грэм
1
Ответ с вознаграждением - это способ решения проблемы, описанной в вопросе. Мой собственный ответ помечен как «правильный», поскольку, хотя иногда проблема вызвана ошибкой приложения в панели запуска (как отмечено в его ответе), моя конкретная проблема была вызвана переключением задач. Решение обеих проблем фиксируется его решением.
Грэм

Ответы:

238

Поведение, которое вы испытываете, вызвано проблемой, которая существует в некоторых средствах запуска Android начиная с API 1. Подробную информацию об ошибке, а также возможных решениях можно найти здесь: https://code.google.com/p/android/issues/ подробно? id = 2373 .

Это относительно распространенная проблема на устройствах Samsung, а также других производителей, которые используют собственный лаунчер / скин. Я не видел, чтобы проблема возникала на стоковой панели запуска Android.

По сути, приложение на самом деле не перезапускается полностью, но ваша активность запуска запускается и добавляется в верхнюю часть стека активности, когда приложение возобновляет запуск. Вы можете подтвердить, что это так, нажав кнопку «Назад», когда вы возобновите приложение и увидите Активность запуска. Затем вы должны будете перейти в действие, которое вы ожидали увидеть при возобновлении работы приложения.

Обходной путь, который я выбрал для решения этой проблемы, заключается в проверке категории Intent.CATEGORY_LAUNCHER и действия Intent.ACTION_MAIN в намерении, которое запускает начальное действие. Если эти два флага присутствуют, и действие не находится в корне задачи (то есть приложение уже запущено), тогда я вызываю finish () для исходного действия. Это точное решение не может работать для вас, но что-то подобное должно.

Вот что я делаю в onCreate () начальной / стартовой активности:

    if (!isTaskRoot()
            && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
            && getIntent().getAction() != null
            && getIntent().getAction().equals(Intent.ACTION_MAIN)) {

        finish();
        return;
    }
starkej2
источник
4
Пока это работает для меня без побочных эффектов пока. Основываясь на логических предположениях, я не вижу причин, почему это не верно.
javahead76
3
Я думаю, что это правильный способ борьбы с этой ошибкой. Работает для меня.
Соколов
3
сделал за меня работу, проверено примерно на 8 разных устройствах. Огромное спасибо!
Шая Айзнер
3
WOOOOOW исправил мою проблему, я искал решение в течение последних 2 часов
Джин Рэймонд Дахер
2
Спасибо @ starkej2. Работал как шарм.
Раджив Саху
55

Этот вопрос по-прежнему актуален в 2016 году. Сегодня тестировщик QA сообщил, что мое приложение перезапускается, а не возобновляет работу со стокового модуля запуска в Android M.

В действительности система добавляла запущенную активность в текущий стек задач , но пользователю показалось, что произошел перезапуск, и они потеряли свою работу. Последовательность была:

  1. Скачать из игрового магазина (или загрузить apk)
  2. Запустите приложение из диалогового окна магазина игр: появится действие A [стек задач: A]
  3. Перейдите к заданию B [стек задач: A -> B]
  4. Нажмите кнопку «Домой»
  5. Запустите приложение из ящика приложения: появится активность A! [стек задач: A -> B -> A] (пользователь может нажать кнопку «Назад», чтобы перейти к операции «B» отсюда)

Примечание: эта проблема не проявляется для отладочных APK, развернутых через ADB, только в APK, загруженных из Play Store или загруженных сбоку. В последних случаях цель запуска из шага 5 содержала флагIntent.FLAG_ACTIVITY_BROUGHT_TO_FRONT , но не в случаях отладки. Проблема исчезнет, ​​как только приложение будет запущено из панели запуска. Я подозреваю, что Задача засевается с искаженным (точнее, нестандартным) намерением, которое препятствует правильному поведению запуска, пока задача не будет полностью очищена.

Я пробовал различные режимы запуска действий , но эти параметры слишком сильно отличаются от стандартного поведения, которого ожидает пользователь: возобновление задачи в действии B. См. Следующее определение ожидаемого поведения в руководстве к Задачам и Back Stack внизу страницы. в разделе «Запуск задачи»:

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

Я нашел этот ответ релевантным и вставил следующее в метод onCreate моего корневого действия (A), чтобы он возобновился соответствующим образом, когда пользователь открывает приложение.

                    /**
     * Ensure the application resumes whatever task the user was performing the last time
     * they opened the app from the launcher. It would be preferable to configure this
     * behavior in  AndroidMananifest.xml activity settings, but those settings cause drastic
     * undesirable changes to the way the app opens: singleTask closes ALL other activities
     * in the task every time and alwaysRetainTaskState doesn't cover this case, incredibly.
     *
     * The problem happens when the user first installs and opens the app from
     * the play store or sideloaded apk (not via ADB). On this first run, if the user opens
     * activity B from activity A, presses 'home' and then navigates back to the app via the
     * launcher, they'd expect to see activity B. Instead they're shown activity A.
     *
     * The best solution is to close this activity if it isn't the task root.
     *
     */

    if (!isTaskRoot()) {
        finish();
        return;
    }

ОБНОВЛЕНИЕ: переместило это решение с флагов анализа паролей к запросам, если действие находится непосредственно в корне задачи. Флаги намерений сложно предсказать и протестировать с помощью различных способов открытия ОСНОВНОЙ операции (запуск из дома, запуск из кнопки «вверх», запуск из Play Store и т. Д.)

Рич Эмер
источник
4
«Проблема исчезнет, ​​как только приложение будет запущено из панели запуска». Это самая странная часть, и я тоже это заметил - убивая приложение после первого запуска, оно снова начинает нормально работать. Такая странная ошибка. Спасибо за тщательный анализ и за решение.
Одед
11
Примечание: вы можете воспроизвести проблему снова после первоначальной очистки («холодный запуск», как вы ее описали), используя «Открыть» на странице приложения Google Play, даже если вы установили APK-файл через Android Studio . Я нашел это очень полезным для проверки исправления.
Одед
Хорошее объяснение!
karanatwal.github.io
Спасибо за это объяснение :)
AndroidEnthusiast
2
Это происходит со многими приложениями. Google Photos является основным, который я протестировал.
Рагхубанш Мани,
19

Ага! (tldr; см. заявления жирным шрифтом внизу)

Я нашел проблему ... Я думаю.

Итак, я начну с предположения. Когда вы нажимаете панель запуска, она либо запускает установку по умолчанию, Activityлибо, если Taskзапущен предыдущий запуск, выводит его на передний план. Другими словами, если на каком-либо этапе навигации вы создаете новый Taskи finishстарый, программа запуска больше не будет возобновлять ваше приложение.

Если это предположение верно, я вполне уверен, что это должно быть ошибкой, учитывая, что каждый Task находится в одном и том же процессе и является таким же допустимым кандидатом на резюме, как и первый созданный?

Моя проблема была исправлена ​​путем удаления этих флагов из пары Intents:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );

Хотя совершенно очевидно, что FLAG_ACTIVITY_NEW_TASKсоздает новое Task, я не оценил, что вышеупомянутое предположение было в силе. Я посчитал это виновником и удалил его для проверки, и у меня все еще была проблема, поэтому я отменил ее. Тем не менее, у меня все еще были следующие условия:

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

Мой экран-заставка запускал «главное» Activityв моем приложении, используя вышеуказанный флаг. В конце концов, если бы я «перезапустил» мое приложение и оно Activityвсе еще работало, я бы предпочел сохранить его информацию о состоянии.

Вы заметите, что в документации не упоминается о запуске нового Task:

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

Например, рассмотрим задачу, состоящую из действий: A, B, C, D. Если D вызывает startActivity () с намерением, которое разрешается к компоненту действия B, то C и D будут завершены, и B получит данное намерение , в результате чего стек теперь: A, B.

Текущий запущенный экземпляр действия B в вышеприведенном примере либо получит новое намерение, которое вы начинаете здесь, в его методе onNewIntent (), либо само завершится и перезапустится с новым намерением. Если он объявил, что его режим запуска является «множественным» (по умолчанию), и вы не установили FLAG_ACTIVITY_SINGLE_TOP в том же намерении, то он будет завершен и создан заново; для всех других режимов запуска или если установлен FLAG_ACTIVITY_SINGLE_TOP, то это намерение будет доставлено в onNewIntent () текущего экземпляра.

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

Итак, у меня была ситуация, описанная ниже:

  • Aзапущен Bс FLAG_ACTIVITY_CLEAR_TOP, Aзаканчивает.
  • Bхочет перезапустить службу, поэтому отправляет пользователя, Aкоторому назначена логика перезапуска службы и пользовательский интерфейс (без флагов).
  • Aзапускается Bс FLAG_ACTIVITY_CLEAR_TOP, Aзаканчивается.

На этом этапе второй FLAG_ACTIVITY_CLEAR_TOPфлаг перезапускается, Bкоторый находится в стеке задач. Я предполагаю, что это должно разрушить Taskи начать новую, вызывая мою проблему, которая является очень сложной ситуацией, если вы спросите меня!

Итак, если все мои предположения верны:

  • LauncherВозобновляется только изначально созданная задача
  • FLAG_ACTIVITY_CLEAR_TOPбудет, если он перезапускает только оставшиеся Activity, также воссоздать новыйTask
Graeme
источник
FLAG_ACTIVITY_CLEAR_TOP не создает новую задачу и не перезапускает действие, которое вы пытаетесь запустить, если это единственное оставшееся действие. "Если установлено, и запускаемое действие уже выполняется в текущей задаче, то вместо запуска нового экземпляра этого действия все остальные действия поверх него будут закрыты, и это намерение будет доставлено в (сейчас сверху) старая деятельность как новый Намерение. "
starkej2
2
Я понимаю, что это не предназначено - но методом проб и ошибок это происходит в моем случае. Удаление этих флагов устраняет проблему.
Грэм
Это не создает идеальное резюме на всех устройствах при любых условиях.
danny117
Удаление флагов устранило проблему для меня. Спасибо
Sealer_05
У меня есть сценарий заставки / основного экрана, но я не использую какие-либо флаги для перехода от заставки к основному, но проблема для меня воспроизводима - это решение не работает для меня.
ROR
12

У меня была такая же проблема на устройствах Samsung. После долгих поисков ни один из этих ответов не помог мне. Я обнаружил, что в файле AndroidManifest.xmllaunchMode установлено значение singleInstance( android:launchMode="singleInstance"). Удаление launchModeатрибута исправило мою проблему.

Али
источник
Действительно, это помогло мне. Я нашел этот ответ из другого SO вопроса, чтобы быть полезным: stackoverflow.com/a/21622266/293280 . И это записывают различные типы launchModeценностей: inthecheesefactory.com/blog/…
Джошуа Пинтер
Это исправление сработало для меня! Это добавлено не в манифесте, а в атрибуте активности над основным классом активности.
Калин Власин
@CalinVlasin не могли бы вы показать мне, как именно вы использовали режим запуска? где вы это разместили? в настоящее время у меня это так, но это вызывает проблему: <активность android: имя = ". UI.landing.MyActivity" android: configChanges = "locale | layoutDirection" android: launchMode = "singleTop" android: windowSoftInputMode = "stateAlwaysHidden | AdjustResize ">
j2emanue
Это тоже была моя проблема. Я думаю, что это должно использоваться в сочетании с принятым ответом (! IsTaskRoot ...
behelit
1

На моем Cat s60 я включил «Не держать действия» в опциях разработчика, отключив это снова, я смог переключать приложения, не теряя состояния приложений ...

PTP
источник
Не знаю, как, но это было включено на моем устройстве.
RealPro
0

Это решение сработало для меня:

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Intent startMain = new Intent(Intent.ACTION_MAIN);
            startMain.addCategory(Intent.CATEGORY_HOME);
            startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(startMain);
            return false;
        }
        else
            return super.onKeyUp(keyCode, event);
    }

кредит: мне нужно свести к минимуму приложение для Android по нажатию кнопки назад

может работать не на всех устройствах, но успешно создает поведение кнопки «Домой» при нажатии кнопки «Назад», что останавливает действие, а не завершает его.

Рассел Чисхолм
источник
Интересный трюк, но не решает проблему, как указано. Очень полезно для других проблем / вопросов, хотя.
Грэм
Кнопка «Назад» имеет определенное назначение, и пользователи ожидают, что кнопка «Назад» будет делать то, что должна. Переопределять это в любом случае неправильно и, на мой взгляд, крайне непрофессионально.
Ошибки происходят
-1

У меня была такая же проблема, причина была:

(Код Котлина, в MainActivity)

override fun onBackPressed() {
    finish()
}

Поэтому при переходе к моей MainActivity из моей LoginActivity я использую это:

    val intent = Intent(this, MainActivity::class.java)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(intent)

При использовании этих флагов мне не нужно иметь onBackPressed () в моей MainActivity, оно естественным образом выйдет из приложения по щелчку назад. А при нажатии кнопки «Домой» и возвращении в приложение оно не перезагружается.

Кристер
источник
-2

Решение для людей, которые не имеют ни малейшего представления о программировании и испытывают эту проблему в своем телефоне Android. Это происходит в основном из-за обновления версии Android (только мое предположение). После обновления все ваши приложения будут оптимизированы, чтобы использовать меньше батареи. Но это, в свою очередь, замедляет работу вашего устройства.

Как решить

Зайдите в настройки >> Приложения >> настройки приложений (ищите значок настроек в любом месте экрана - он отличается на разных устройствах) >> оптимизация батареи (или аналогичный параметр [введите описание изображения здесь] [1] вкл.) >> переместить все приложения в «неоптимизированном» состоянии (приходится делать 1 на 1 вручную - может быть разрешено / запрещено на некоторых телефонах). Ваше приложение запуска должно быть «неоптимизировано» (в моем случае это средство запуска Zen UI - я думаю, это виновник - вы можете попробовать оптимизировать / не оптимизировать и перезапустить другое приложение, если у вас есть время). Теперь перезагрузите телефон. (нет необходимости сбрасывать данные / безопасный режим или какие-либо проблемы)

Попробуйте многозадачность сейчас. :) Нажатие на значок запуска теперь должно привести к возобновлению текущей задачи. :) Ваше устройство станет Не беспокойтесь о батарее, он все равно будет разряжаться.

Арун Антоний
источник
-8

Бесценный для ваших пользователей. Идеальное резюме даже после нескольких недель сидения в списке недавно использованных приложений.

Это выглядит как резюме для пользователя, но на самом деле это полноценный старт.

Background: память, используемая приложениями, которые в основном действии не запускали задачу, легко восстановить. ОС может просто перезапустить приложение с исходным пакетом, переданным в onCreate. Однако вы можете добавить исходный пакет вonSaveInstanceState чтобы при перезапуске приложения ОС вы могли восстановить состояние экземпляра, и никто не знает, будет ли приложение перезапущено или возобновлено. Взять, к примеру, программу классической карты. Пользователь перемещается в позицию на карте и затем нажимает клавишу возврата. Две недели спустя это картографическое приложение все еще находится в списке последних приложений наряду с Facebook, Pandora и Candy. ОС не только сохраняет название приложения для недавно использованных приложений, но и сохраняет исходный пакет, использованный для запуска приложения. Однако программист закодировалonSaveInstanceState метод, поэтому оригинальный пакет теперь содержит все материалы и информацию, необходимые для создания приложения, чтобы оно выглядело, как будто оно было возобновлено.

Пример: сохраните текущую позицию камеры в onSaveInstanceState, только если приложение выгружено и должно быть перезапущено несколько недель спустя из списка последних приложений.

@Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        // save the current camera position;
        if (mMap != null) {
            savedInstanceState.putParcelable(CAMERA_POSITION,
                    mMap.getCameraPosition());
        }
    }



@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // get the exact camera position if the app was unloaded.
        if (savedInstanceState != null) {
            // get the current camera position;
            currentCameraPosition = savedInstanceState
                    .getParcelable(CAMERA_POSITION);
        }

Примечание: вы также можете использовать onRestoreInstanceStateметод, но мне легче восстановить экземпляр в onCreate.

Это более чем вероятно, что происходит в вашем приложении. На некоторых устройствах ваше приложение выгружается в свободную память. Да, есть некоторые флаги, которые помогают, но флаги не улавливают каждый нюанс вашего приложения, и флаги не будут поддерживать вас в течение многих недель, как onSaveInstanceStateбудет. Вы должны закодировать идеальное резюме через две недели. Это сложная задача для сложного приложения, но мы позади вас и готовы помочь.

Удачи

danny117
источник
Это не связано с общими проблемами с памятью или условиями «состояния паузы» по умолчанию. Я пробовал мое приложение на многих устройствах (некоторые с большой памятью, некоторые с меньшей памятью).
Грэм,
Вот почему вы сохраняете постоянные данные в onPause. Я пытался возобновить работу моего приложения в течение двух недель на телефоне, которым я пользуюсь каждый день, и позвольте мне сообщить вам, что через две недели вызывается метод onCreate, и ОС передает пакет, который я сохранил. onSaveSessionState и я использую данные в пакете, чтобы моя активность выглядела точно так, как я ее покинул. Так что с момента моего ответа прошло всего три дня, поэтому вы никак не могли бы пройти двухнедельный тест. Резюме: приложение может быть закрыто в любое время в фоновом режиме.
danny117
@ danny117 Не думаю, что вы точно понимаете проблему, с которой сталкивается Грэм
starkej2
Я понимаю проблему Грэма. На некоторых устройствах возобновление работы приложения вызывает onCreate. Звучит так же, как мой HTC EVO (Gingerbread), который убил бы приложение, просто чтобы повернуть экран. Посмотрите на документах он говорит , так что приложение может быть восстановлено в OnCreate developer.android.com/reference/android/app/...
danny117
@ danny117 это правильно, но проблема, с которой он сталкивается, не связана с повторным созданием одного действия при возобновлении приложения (что ожидается), но запускается неправильное действие
starkej2