Почему спустя время приложение перезапускается, если его свернуть?
Работаю с картой. Если свернуть приложение и развернуть его спустя некоторое время, приложение перезапускается. Читал, что если возможность отключить такое поведение, но не могу найти как.
Отслеживать
51.6k 200 200 золотых знаков 61 61 серебряный знак 242 242 бронзовых знака
задан 19 авг 2021 в 6:35
Svetl9chok Svetl9chok
433 2 2 серебряных знака 13 13 бронзовых знаков
Отключить такое поведение нельзя, так работает система Андроид. Она может прибивать ваше приложение, когда вздумается. Наоброт, чтобы сымитировать такое поведение, во время разработки и тестирования полезно включать Don’t keep activities в настройках разработчика.
19 авг 2021 в 11:56
Если вы хотите восстанавливать состояние вашей Активити, то я в своем ответе привел один из способов, как это можно сделать.
19 авг 2021 в 11:58
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Система Андроид не гарантирует, что приложение не будет выгружено из памяти. Для того, чтобы не терять состояние, необходимо его сохранять.
Рассмотрим простой пример. На экране текст и кнопка. Нажатие на кнопку увеличивает счетчик counter на единицу и вставляет в текст.
public class MainActivity extends AppCompatActivity < private TextView textView; private Button button; private int counter; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button.setOnClickListener(v ->< counter++; updateViews(); >); > private void updateViews() < textView.setText(String.valueOf(counter)); >>
Каждое нажатие на кнопку — это пользовательский ввод, к нему нужно относиться бережно и не терять то, что вводил пользователь.
Если мы свернем приложение, система Андроид может прибить процесс нашего приложения, если посчитает нужным (например, если на телефоне будет заканчиваться память). Пользовательский ввод будет утерян безвозвратно.
Чтобы устранить этот недостаток, в Активити переопределяем onSaveInstanceState() и сохраняем все, что нам нужно. В нашем случае это текущее состояние счетчика:
@Override protected void onSaveInstanceState(Bundle state)
Восстанавливаем значение счетчика в методе onCreate() :
@Override protected void onCreate(Bundle savedInstanceState) < . if (savedInstanceState != null) < counter = savedInstanceState.getInt("com.example.counter"); updateViews(); >>
Если вы хотите проверить, как будет вести приложение при нехватке памяти, включите на устройстве режим Don’t keep activities («Не сохранять операции») в настройках разработчика.
В таком режиме вы сразу увидите все недостатки вашего приложения при сворачивании и разворачивании.
Как отключить опцию «Do not keep activities» в смвртфоне на базе Андроид?
На смартфоне НТС выскакивает окно с такой надписью: The option «Do not keep activities» (also called «immediately destroy activities») is set ON in Android Developer Options. The option «Do not keep activities» is a developer option and should not be set under normal user-conditions. Not run with this option set ON.
Телефон ребёнка. Может он загрузил чего не то или накрутил не там. Я не нашёл где она отключается.
Голосование за лучший ответ
настройки — функции для разработчиков — сними все галочки — перезагрузи девайс
Do not keep activities
«Не сохранять операции» — именно таким странным образом переведена фраза «Do not keep activities» в настройках Android. А описание «Удалять все операции сразу после их завершения пользователем» не добавляет ясности. Включается она в меню «Параметры разработчика» (Developer Options), находится в самом низу.
Работает эта настройка очень просто, когда она включена, все неактивные активити умирают. Т.е. после перехода из активити A в активити B, активити A уничтожается. Таким образом можно проверить насколько вписывается ваше приложение в activity lifecycle.
Не хочется вдаваться в подробности lifecycle, но, в двух словах, система убивает фоновую активити когда захочет. В реальности это происходит не так редко, так что не стоит этим пренебрегать. Например, вам кто-то позвонил. Во время вашего разговора система может убить активити приложения, которым вы только что пользовались.
Или, самый простой способ убить активити — повернуть экран (если не android:configChanges=«keyboardHidden|orientation|screenSize»). Но не все приложения поддерживают поворот экрана. Да и те, которые поддерживают, встречаются с ошибками lifecycle на этапе разбработки, так что у них как раз проблем и не должно быть. Второй простой способ — сменить язык устройства.
Ну и разумеется можно просто включить «Do not keep activities».
Я потратил несколько дней на исправление ошибок, после того как прошелся по своему приложению с «Do not keep activities». Теперь, думаю, стоит всегда включать эту настройку на время разработки. Главной моей проблемой было корректное подключение социальных сетей в приложении.
Почти не возникло проблем с Вконтакте, с их SDK можно написать все правильно, но все таки есть у них небольшая проблема. А именно, после авторизации через приложение, результат возвращается в onActivityResult. По документации, нужно вызвать
VKUIHelper.onActivityResult(requestCode, resultCode, data);
однако, в случае уничтожения активити нашего приложение, вылетит NPE, поэтому сначала нужно вызвать
VKUIHelper.onResume(this);
Не совсем очевидно, да и незадокументировано. Завел issue, на всякий.
Были проблемы с Facebook, потому как, по глупости своей, я решил использовать android-simple-facebook, а ее автор, как я понял, не особо парился по поводу android lifecycle. Пришлось полностью от него отказаться и переделывать все на официальный SDK Facebook и все проблемы ушли. Хотя, наверное, можно было и так все поправить одной строчкой, но пути назад уже не было.
И совсем не было проблем с Twitter, потому что все работает просто, как топор. У них просто нет своего SDK и авторизации через приложение. Приходится делать все по старинке, через WebView. Вообще у них самая ужасная авторизация из всех.
Ну да это так, лирическое отступление.
Есть проблемы с PayPal SDK, оно просто валится везде где только можно. Благо кто-то уже завел issue до меня и наверняка они в скором будущем это поправят.
Другие примеры
У меня не много приложений на телефоне, да и смотреть их все мне, если честно, лень, но я не мог пройти мимо недавно вышедшего приложения хабра. Начнем, конечно же, с него. Ну и добавлю ошибку Payoneer, которую нашел случайно:)
Хабрахабр
Я потыкался по разным экранам и уже было расстроился, неужели все написано правильно и один лишь я делаю столько ошибок. Но нет, я таки добрался до страницы About:
Caused by: java.lang.NullPointerException at ru.habrahabr.activity.about.AboutFragment.onAttach(AboutFragment.java:44)
Payoneer
Много времени не понадобилось. Экран логина, ушел в keepass за паролем, вернулся — гипс крэш:
Caused by: java.lang.NullPointerException at com.payoneer.android.ui.fragment.LoginFragment.initializeOnCreateData(LoginFragment.java:379) at com.payoneer.android.ui.fragment.LoginFragment.onCreate(LoginFragment.java:152)
Ещё есть некоторые приложения без крэшэй, но с неправильным поведением, например, не сохраняется открытый фрагмент, а вместо него, после пересоздания, снова показывается главный экран приложения.
P.S.
Используйте настройку «Do not keep activities», надеюсь для кого-то эта информация окажется новой и поможет избежать ошибок поведения и крэшэй.
Думаю тоже смог бы избежать кучи репортов в Google Play, если бы сразу знал об этой настройке.
Ах да, репорты отправил.
- android development
- android
- appgranula
H Как не потерять связь между фоновыми задачами и Activity в черновиках Из песочницы
При программирование для Android есть два основных подхода к управлению состоянием Activity, View или Fragment.
Первый подход — это игнорировать состояние и загружать содержимое заново при каждом создании (поворот экрана, переключение между приложениями).
Второй подход — сохранять и восстанавливать состояние компонентов в соответствии с шаблоном onSaveInstanceState/onRestoreInstanceState.
Первый подход легко реализовать, но пользовательский опыт оставляет желать лучшего.
Второй подход влечет за собой огромное количество проблем. Это реально трудно — поддерживать все компоненты в полной готовности к сохранению/восстановлению и одновременно с этим выполнять в фоне какие-то задачи. Давайте рассмотрим некоторые из проблем, которые при этой возникают.
1. Когда Fragment или Activity запускает фоновую задачу (например, Activity хочет подтянуть содержимое из интернета), результат такой задачи нельзя доставить обратно в Activity, если Activity было уничтожено или пересоздано из-за изменения ориентации экрана или перезапуска процесса.
2. Разрекламированная функция Fragment.setRetainInstance(true) тут не поможет, потому что такой Fragment сохраняется только для изменений конфигурации экрана. В добавок, хранение ссылок на Fragment может привести к утечкам памяти.
3. Не существует простого способа проверить, запущена ли уже фоновая задача. Обычный способ — это проверить запускалась ли задача ранее при помощи savedInstanceState, и если не запускалась — тогда запустить фоновую задачу. Но, если фоновая задача была потеряна при пересоздании процесса, она не запустится второй раз, и пользователь получит экран со значком вечного прогресса. Такой глюк — совершенно обычное дело и встречается даже в самих популярных приложениях.
4. Сохранение списка фоновых задач в статических переменных или в объекте Application — хорошая идея, но это на самом деле не спасет, если они будут сброшены при перезапуске процесса из-за нехватки памяти.
Состояние фоновых задач и состояния Activity должны быть согласованны.
Есть ли какое-нибудь решение для всех этих проблем?
Решение
С одной стороны, у нас есть Activity, которая сохраняет свое состояние, а с другой у нас есть статические переменные. Иногда Activity выживает, а иногда выживает процесс и статические переменные.
Решение — это хранить все фоновые задачи в статических переменных и доставлять результаты их выполнения в Activity. Если Activity не находится в активном состоянии (между onResume/onPause), то результаты нужно придержать, пока Activity не активизируется.
Параллельно, нужно сохранять/восстанавливать список фоновых задач для Activity, потому что статические переменные могут быть потеряны из-за перезапуска процесса. Тогда эти фоновые задачи нужно будет перезапустить.
Вот так и появился AsyncBean.
Как использовать AsyncBean
public class YourAsyncTaskBean extends AsyncBean < YourAsyncTaskBean() < >@Override protected void run(boolean restart) < // Запустить фоновую задачу. Когда завершена, фоновая задача // должна вызвать AsyncBean.deliver() в главном потоке. deliver(); >> // где-то в объявлении activity/fragment/view YourAsyncTaskBean yourBean; // где-то в activity/fragment/view onCreate/onRestoreInstanceState if (savedInstanceState != null) yourBean = AsyncBean.restoreInstance((AsyncBean)savedInstanceState.getSerializable("yourBean")); // как запустить yourBean = new YourAsyncTaskBean(); yourBean.execute(yourBeanListener); // код сохранения/подключения/отключения к фоновой задаче @Override protected void onSaveInstanceState(Bundle outState) < super.onSaveInstanceState(outState); outState.putSerializable("yourBean", yourBean); >@Override protected void onResume() < super.onResume(); if (yourBean != null) yourBean.onResume(yourBeanListener); >@Override protected void onPause() < super.onPause(); if (yourBean != null) yourBean.onPause(); >// для получения данных после выполнения фоновой задачи используйте AsyncBeanListener yourBeanListener = new AsyncBeanListener() < @Override public void onAsyncBeanStateChanged(AsyncBean bean, AsyncBeanState state) < if (bean instanceof YourAsyncTaskBean && state == AsyncBeanState.COMPLETED) . >>;
Здесь много вспомогательного кода, так что сделайте базовый класс, который будет управлять всем этим вместо вас, как я сделал это в BaseActivity в демонстрационном приложении.
Демонстрационное приложение
Присутствует apk, можно сразу запускать. Разрешений не требует.
Этот пример поддерживает две фоновые задачи при поворотах экрана и пересоздании Activity, одновременно показывая индикатор прогресса. Когда процесс уничтожается и Activity пересоздается из сохраненного состояния, фоновые задачи автоматически перезапускаются. Состояние фоновых задач и состояние Activity поддерживаются в согласованном состоянии.
Демонстрационное приложение должно корректно работать при:
1. Пересоздании Activity — а) поверните экран или б) откройте настройки разработчика и включите галочку «Do not keep activities» («Не сохранять операции» на русском). Переключайтесь между приложениями во время выполнения фоновых задач. Выполнение фоновых задач не должно прерываться.
2. Пересоздание процесса — откройте диспетчер задач и нажмите «Очистить память». Переключитесь в демонстрационное приложение, вы увидите, как задачи будут перезапущены.
Заключение
AsyncBean заполняет большую брешь в архитектуре Android-приложения.
Комментарии и предложения — приветствуются!
