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

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

Проблема:

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

Деталь:

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

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

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

  • В Motorola Xoom, когда вы нажимаете значок запуска, приложение всегда запускает начальный запуск Activity независимо от того, что в данный момент выполняется. Я предполагаю, что значки запуска всегда начинаются с намерения "LAUNCHER".

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

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

Edit:

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

Ответ 1

Ага! (TL;DR; см. инструкции, выделенные полужирным шрифтом внизу)

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

Итак, я начну с предположения. Когда вы нажимаете кнопку запуска, она либо запускает по умолчанию Activity, либо, если a 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, у которого есть логика перезапуска службы и UI (флаги отсутствуют).
  • A запускает B с завершением FLAG_ACTIVITY_CLEAR_TOP, A.

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

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

  • Launcher только возобновляет первоначально созданную задачу
  • FLAG_ACTIVITY_CLEAR_TOP будет, если он перезапустит единственный оставшийся Activity, также заново создайте новый Task

Ответ 2

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

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

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

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

Вот что я делаю в onCreate() начального/запуска Activity:

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

        finish();
        return;
    }

Ответ 3

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

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

  • Загрузка из магазина воспроизведения (или sideload apk)
  • Запустить приложение из диалогового окна магазина воспроизведения: появляется действие A [стек задач: A]
  • Перейдите к действию B [стек задач: A → B]
  • Нажмите кнопку "Главная"
  • Запустите приложение из ящика приложений: появится действие A! [task stack: A → B → A] (пользователь может нажать кнопку "Назад", чтобы перейти к активности "B" здесь)

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

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

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

Я нашел этот ответ, чтобы быть релевантным, и ввел в метод 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;
    }

UPDATE: отменило это решение от флажков разбора синтаксического анализа до запроса, если действие находится непосредственно в основе задачи. Флаги Intent трудно предсказать и протестировать со всеми различными способами открытия MAIN-активности (запуск из дома, запуск с кнопки "вверх", запуск из Play Маркета и т.д.).

Ответ 4

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

Ответ 5

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

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

кредит: Мне нужно свернуть приложение андроида на кнопке "Назад"

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

Ответ 6

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

Он выглядит как резюме для пользователя, но на самом деле начинается полномасштабное начало.

Фон: Память, используемая приложениями, которые находятся в основной деятельности, которые не запускали задачу, легко восстановить. ОС может просто перезапустить приложение с исходным пакетом, переданным onCreate. Однако вы можете добавить исходный пакет в onSaveInstanceState, поэтому, когда ваше приложение перезагружается операционной системой, вы можете восстановить состояние экземпляра, и никто не станет мудрее о том, перезапустили или возобновили приложение. Возьмем, к примеру, программу классической карты. Пользователь перемещается в позицию на карте, а затем нажимает на главный ключ. Через две недели это приложение сопоставления по-прежнему входит в список последних приложений, а также facebook, pandora и candy crush. ОС не просто сохраняет имя приложения для недавно использованных приложений, но также сохраняет исходный пакет, используемый для запуска приложения. Однако программист закодировал метод onSaveInstanceState, поэтому пакет orignal теперь содержит все материалы и информацию, необходимые для создания приложения, поэтому похоже, что он был возобновлен.

Пример: Сохраняя текущую позицию камеры в 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. Вы должны закодировать идеальные две недели спустя возобновить. Это непростая задача для сложного приложения, но мы позади вас и здесь можем помочь.

Удача