Програма перезавантажується, а не поновлюється


195

Сподіваюсь, хтось може допомогти мені розібратися, якщо не рішення, то хоча б пояснення поведінки.

Проблема:

На деяких пристроях натискання піктограми запуску призводить до відновлення поточної задачі, на інших це призводить до запуску початкового наміру запуску (ефективно перезапуск програми). Чому це відбувається?

Деталі:

Коли ви натискаєте "Піктограму запуску", програма запускається нормально. Тобто, я припускаю, запускається інтенція з назвою вашого першого Activityз дією android.intent.action.MAINта категорією android.intent.category.LAUNCHER. Однак це не завжди може бути таким:

Якщо на більшості пристроїв натискаєте піктограму запуску після запуску програми, поточна активність у цьому процесі відновлюється ( НЕ початкова Activity). Він поновлюється так само, як якщо б ви вибрали його з "Останні завдання" в меню ОС. Такої поведінки я хочу на всіх пристроях.

Однак на вибраних інших пристроях трапляється різна поведінка:

  • У Motorola Xoom, коли ви натискаєте піктограму запуску, додаток завжди запускатиме початковий запуск Activityнезалежно від того, що зараз працює. Я припускаю, що піктограми пускового пристрою завжди запускають наміри "LAUNCHER".

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

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

Редагувати:

Після останнього запуску цього додатка ми виявляємо, що така поведінка почала відбуватися на всіх пристроях після першого перезавантаження. Що мені здається божевільним, але переглядаючи процес перезавантаження, я фактично не можу знайти те, що відбувається не так.


1
Це може здатися тривіальним питанням, але ви встановили "Не продовжуйте діяльність" істинним у розробці варіантів для Xoom?
Ендрю Шустер

Ні (я бажаю! :)) - я записав життєвий цикл кожної діяльності та діяльності у фоновому режимі як доступні (Вони зупинені - не знищені). Здається, ОС закликає finish()їх у тих випадках, коли вона починається спочатку Activityзнову, а не поновлюється.
Graeme

1
Якщо ви натиснули домашню кнопку, а потім натисніть на піктограму запуску, поведінка резюме є типовою для Android, як ви, напевно, знаєте. Однак якщо натиснути кнопку "назад", щоб повернутися на головний екран, більшість телефонів закінчить додаток. Чи можливий різний метод, який ви використовуєте для виходу з програми, на різних пристроях? Не могли б ви вийти з onKeyUpEvent, щоб перевірити, чи деякі вони не поводяться з жорсткими / м'якими клавішами дивним чином?
Нік Кардосо

2
Ні - я впевнений у проблемі, як сказано вище. Використовуючи домашню програму, щоб застосувати програму у фоновому режимі (не назад, що ви праві, закінчите () активність). На Xoom можна відновити додаток зі списку завдань (тільки не з програми Launcher), тому задні версії точно не загинули.
Graeme

1
Відповідь з нагородою - це спосіб виправити проблему, описану в питанні. Я вказав власну відповідь як "правильну", тому що, хоча інколи проблема викликана помилкою програми у запуску (як зазначено у його відповіді), моя особлива проблема була викликана переключенням завдань. Вирішення обох завдань фіксується його рішенням.
Graeme

Відповіді:


238

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

Це порівняно поширена проблема на пристроях Samsung, а також інших виробників, які використовують користувацький пусковий апарат / шкуру. Я не бачив, щоб ця проблема виникала на пусковій установці Android.

По суті, програма насправді не повністю перезапускається, але запуск запуску "Активність" запускається і додається до вершини стека "Діяльність", коли програма відновиться. Ви можете підтвердити це так, натиснувши кнопку "Назад", коли ви відновите додаток і буде показано активність запуску. Тоді вас слід залучити до активності, яку, як ви очікували, буде показано під час відновлення програми.

Вирішення проблеми, яку я вирішив застосувати, щоб вирішити цю проблему, - це перевірити наявність наміру.CATEGORY_LAUNCHER категорії та дії Intent.ACTION_MAIN у намірі, який запускає початкову діяльність. Якщо ці два прапори присутні, а Діяльність не знаходиться в корені завдання (тобто додаток вже запущений), я закликаю закінчити () на початковій діяльності. Це точно рішення може не підійти для вас, але щось подібне повинно бути.

Ось що я роблю в onCreate () початкової / запускової діяльності:

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

        finish();
        return;
    }

4
Поки що це працює для мене без шкідливих побічних ефектів до цих пір. Виходячи з логічних припущень, я не бачу причини, щоб це не було дійсним.
javahead76

3
Я думаю, що це правильний спосіб боротьби з цією помилкою. Працює для мене.
Соколов

3
зробив роботу для мене, перевірив приблизно на 8 різних пристроях. дуже дякую!
shaya ajzner

3
WOOOOOW вирішив свою проблему, я останні два години шукав виправлення
Жан Реймонд Дахер

2
Дякую @ starkej2 Працював як шарм.
Радєєв Саху

55

Це питання залишається актуальним у 2016 році. Сьогодні тестувач з питань контролю якості повідомив про те, що додаток про перезапуск шахти замість того, щоб відновитись із запуску запасів в 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. Дивіться наступне визначення очікуваної поведінки в посібнику із завдань та зворотного стеку внизу сторінки у розділі "Початок завдання":

Цей фільтр намірів призводить до того, що піктограма та мітка для активності відображаються у запуску програм, даючи користувачам спосіб запустити діяльність та повернутися до завдання, яке воно створює будь-який час після її запуску.

Я визнав цю відповідь актуальною і вставив наступне в метод «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

FLAG_ACTIVITY_CLEAR_TOP не створює нове завдання або не перезапускає діяльність, яку ви намагаєтеся запустити, якщо це єдиний, що залишився активність. "Якщо встановлено, а запущена діяльність вже виконується в поточному завданні, то замість запуску нового примірника цієї активності всі інші дії поверх неї будуть закриті, і цей Намір буде доставлений до (зараз вгорі) стара активність як новий Намір ".
starkej2

2
Я усвідомлюю, що це не призначено - але через спроби та помилки це відбувається саме в моєму випадку. Видалення цих прапорів усуває проблему.
Graeme

Це не створює ідеального резюме на всіх пристроях за будь-яких умов.
danny117

Видалення прапорів вирішило проблему для мене. Спасибі
Sealer_05

У мене є сценарій сплеску / головний екран, але я не використовую жодних прапорів для переходу від сплеску до основного, все-таки ця проблема відтворюється для мене - це рішення не працює для мене.
ROR

12

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


Дійсно, це і зробило трюк для мене. Цю відповідь я знайшов із іншого питання ПС як корисний: stackoverflow.com/a/21622266/293280 . І це записування різних типів launchModeзначень: inthecheesefactory.com/blog/…
Джошуа Пінтер

Це виправлення працювало для мене! Додано це не в маніфест, а в атрибут діяльності над основним класом діяльності.
Калін Власін

@CalinVlasin ви могли б мені показати, як саме ви використовували запускMode? куди ти її розмістив? в даний час у мене це є, але це викликає проблему: <активність android: name = ". UI.landing.MyActivity" android: configChanges = "locale | layoutDirection" android: launchMode = "singleTop" android: windowSoftInputMode = "stateAlwaysHidden | prilagodResize ">
j2emanue

Це була і моя проблема. Я думаю, це слід використовувати разом з прийнятою відповіддю (! IsTaskRoot ...
behelit

1

На моєму Cat s60 я включив опцію "Не продовжувати діяльність" в параметрах для розробників, відключивши це, знову дозволив мені перемикати програми, не втрачаючи стан додатків ...


Поняття не маю як, але це було ввімкнено на моєму пристрої.
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 при натисканні кнопки "назад"

може працювати не на всіх пристроях, але успішно створює поведінку кнопки будинку при натисканні кнопки "назад", тим самим зупиняючи діяльність, а не закінчуючи її.


Цікавий трюк, але не вирішує проблему, як заявлено. Дуже корисно для інших проблем / питань.
Graeme

Кнопка "Назад" має певну мету, і користувачі очікують, що кнопка "Назад" зробить те, що вона повинна робити. Переосмислити його будь-яким чином неправильно і, на мою думку, вкрай непрофесійно.
Помилки трапляються

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

Під час використання цих прапорів мені не потрібно мати onBackPress () в моєму MainActivity, він вийде з програми, природно, при натисканні назад. А при натисканні кнопки "Домашня сторінка" та поверненні в додаток не перезапускається.


-2

Рішення для людей, які не мають уявлення про програмування і відчувають цю проблему у своєму телефоні Android. Це відбувається в основному через оновлення версії для android (лише моє припущення). Після оновлення всі ваші програми будуть оптимізовані для використання менше акумулятора. Але це в свою чергу сповільнює ваш пристрій.

Як вирішити

Перейдіть до налаштувань >> Програми >> налаштування додатків (шукайте знак налаштування в будь-якому місці на екрані; він різний на різних пристроях) >> оптимізація батареї (або подібні опті [введіть опис зображення тут] [1] вкл.) >> перемістити все програми в стані "не оптимізовано" (потрібно робити 1 на 1 вручну - можна дозволити / заборонити на деяких телефонах). Ваш додаток запуску потрібно "не оптимізувати" (в моєму випадку запуску дзеркального інтерфейсу в інтернеті - я вважаю винуватцем цього вину. Ви можете спробувати оптимізувати / Не оптимізувати та перезапустити інший додаток, якщо у вас є час). Тепер перезавантажте телефон. (не потрібно скидати дані / безпечний режим чи будь-які проблеми)

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


-8

Безцінний для ваших користувачів. Ідеальне резюме навіть після тижнів сидіння у списку нещодавно використаних програм.

Це схоже на резюме для користувача, але насправді це повноцінний початок.

Передумови: Пам'ять, яку використовують додатки, які є основною діяльністю та не розпочали завдання, легко відновити. Ос може просто перезапустити додаток з оригінальним пакетом, переданим на onCreate. Однак ви можете додати до оригінального пакета, onSaveInstanceStateтому, коли ваш додаток перезапущено ОС, ви можете відновити стан екземпляра, і ніхто не мудріший щодо того, перезапустити чи відновити додаток. Візьмемо для прикладу класичну програму карт. Користувач переходить до положення на карті, а потім натискає домашню клавішу. Через два тижні це додаток для відображення все ще в списку останніх програм, а також фейсбук, пандора та цукерки. ОС не просто зберігає назву програми для нещодавно використаних додатків, але також зберігає оригінальний пакет, який використовується для запуску програми. Однак програміст зашифрував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будуть. Вам доведеться кодувати ідеальні два тижні пізніше. Це складне додаток буде непростим завданням, але ми за вами і ми тут, щоб допомогти.

Щасти


Це не пов'язано із загальними проблемами пам'яті або умовами "стану паузи" за замовчуванням. Я спробував свою програму на багатьох пристроях (деякі з великою пам’яттю, інші з меншою пам’яттю).
Graeme

Ось чому ви зберігаєте постійні дані в onPauseI, я спробував відновити моє додаток аж через два тижні на телефоні, яким я користуюся щодня, і дозвольте мені сказати вам через два тижні, як викликається метод onCreate, і OS передається в пакеті, в якому я зберегся onSaveSessionState, і я використовую дані в групі, щоб моя діяльність відображалася саме так, як я її покинув. Тож минуло три дні з моєї відповіді, тому немає можливості ви пройшли тест на два тижні. Резюме: Додаток можна вимкнути будь-коли, коли він знаходиться у фоновому режимі.
danny117

@ danny117 Я не думаю, що ви точно розумієте проблему, з якою переживає Graeme
starkej2

Я розумію питання Грейма. На деяких пристроях відновлення дзвінків у програмі onCreate. Звучить точно так само, як мій HTC EVO (Пряники), який би вбивство програми лише для обертання екрана. Подивіться на документи, в яких написано, щоб додаток можна було відновити в onCreate developer.android.com/reference/android/app/…
danny117

@ danny117 це правильно, але проблема, з якою він виникає, не пов’язана з відновленням однієї активності, коли додаток відновлюється (що очікується), але починається неправильна діяльність
starkej2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.