2018: Android поддерживает это изначально с помощью компонентов жизненного цикла.
Март 2018 ОБНОВЛЕНИЕ : теперь есть лучшее решение.См. ProcessLifecycleOwner .Вам нужно будет использовать новые компоненты архитектуры 1.1.0 (самые последние на данный момент), но он специально предназначен для этого.
В этом ответе есть простой пример , но я написал пример приложения и сообщение в блоге об этом.
С тех пор, как я написал это в 2014 году, возникли разные решения.Некоторые работали, некоторые считали работающими , но имели недостатки (включая мою!), И мы, как сообщество (Android), научились справляться с последствиями и писали обходные пути для особых случаев.
Никогда не думайте, что единственный фрагмент кода - это решение, которое вы ищете, это маловероятно;еще лучше, попытайтесь понять, что он делает и почему он это делает.
Класс MemoryBoss
на самом деле никогда не использовался мной, как написано здесь, это просто кусок псевдокода, который сработал.
Если только у вас нет веской причины не использовать компоненты новой архитектуры (а некоторые есть, особенно если вы используете супер старые apis), тогда используйте их.Они далеки от совершенства, но ни один из них не был ComponentCallbacks2
.
ОБНОВЛЕНИЕ / ЗАМЕТКИ (ноябрь 2015 г.) : Люди делали два комментария, во-первых, вместо этого следует использовать >=
==
, поскольку в документации указано, что не следует проверять точные значения .Это нормально для большинства случаев, но имейте в виду, что если вы только хотите сделать что-то , когда приложение перешло в фоновый режим, вам придется использовать == и также объедините его с другим решением (например, обратными вызовами Activity Lifecycle), иначе вы можете не получить желаемого эффекта.Пример (и это случилось со мной) заключается в том, что если вы хотите заблокировать ваше приложение с экраном пароля, когда оно переходит на задний план (например, 1Password, если вы знакомы с ним), вы можете случайно заблокироватьваше приложение, если у вас мало памяти и вы неожиданно тестируете на >= TRIM_MEMORY
, потому что Android вызовет LOW MEMORY
вызов, и это выше, чем у вас.Так что будьте осторожны, как / что вы тестируете.
Кроме того, некоторые люди спрашивают, как определить, когда вы вернетесь.
Простейший способ, которым я могу придумать, объяснен ниже, но, поскольку некоторые люди не знакомы с ним, я добавляю здесь псевдокод.Предполагая, что у вас есть YourApplication
и MemoryBoss
классы, в вашем class BaseActivity extends Activity
(вам нужно будет создать один, если у вас его нет).
@Override
protected void onStart() {
super.onStart();
if (mApplication.wasInBackground()) {
// HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
mApplication.setWasInBackground(false);
}
}
Я рекомендую onStart, потому что диалоги могут приостанавливатьсятак что держу пари, что вы не хотите, чтобы ваше приложение считало «оно ушло в фон», если все, что вы делали, это отображали полноэкранное диалоговое окно, но ваш пробег может отличаться.
И это все.Код в блоке if будет выполняться только один раз , даже если вы перейдете к другому действию, новое (которое также extends BaseActivity
) сообщит, что wasInBackground
равно false
, поэтому не будетвыполнить код, , пока не будет вызван onMemoryTrimmed
и флаг снова не будет установлен в true .
Надеюсь, это поможет.
ОБНОВЛЕНИЕ / ЗАМЕТКИ (апрель 2015 г.) : Перед тем, как начать копирование и вставку этого кода, обратите внимание, что я обнаружил пару случаев, когда он может быть ненадежным на 100% и необходимо комбинировать с другими методами для достижения наилучших результатов.Примечательно, что есть два известных случая , в которых обратный вызов onTrimMemory
не гарантированно будет выполнен:
Если ваш телефон блокирует экран, когда ваше приложение видно(скажем, ваше устройство блокируется через nn минут), этот обратный вызов не вызывается (или не всегда), потому что экран блокировки находится только сверху, но ваше приложение все еще «работает», хотя и закрыто.
Если у вашего устройства относительно мало памяти (и она находится под нагрузкой), операционная система, похоже, игнорирует этот вызов и сразу переходит к более критическим уровням.
СейчасВ зависимости от того, насколько важно для вас знать, когда ваше приложение перешло в фоновый режим, вам может понадобиться, а может и не понадобиться расширять это решение вместе с отслеживанием жизненного цикла действия и так далее.
Просто имейте это в виду и создайте хорошую команду для проверки качества;)
КОНЕЦ ОБНОВЛЕНИЯ
Возможно, уже поздно, но есть надежный методв Ice Cream Sandwich (API 14) и выше .
Оказывается, что когда в вашем приложении больше нет видимого пользовательского интерфейса, вызывается обратный вызов.Обратный вызов, который вы можете реализовать в пользовательском классе, называется ComponentCallbacks2 (да, с двумя).Этот обратный вызов доступен только в API Level 14 (Ice Cream Sandwich) и выше.
Вы в основном получаете вызов метода:
public abstract void onTrimMemory (int level)
Уровень20 или более конкретно
public static final int TRIM_MEMORY_UI_HIDDEN
Я тестировал это, и оно всегда работает, потому что уровень 20 - это просто "предложение", что вы можете захотеть освободить некоторые ресурсы, так как ваше приложение больше не видно.
Чтобы процитировать официальные документы:
Уровень onTrimMemory (int): процесс показывал пользовательский интерфейс и больше не делает этого.Большие выделения с пользовательским интерфейсом должны быть освобождены в этот момент, чтобы обеспечить лучшее управление памятью.
Конечно, вы должны реализовать это, чтобы фактически делать то, что он говорит (очистить память, которая не использоваласьв определенное время очистите некоторые коллекции, которые оставались неиспользованными и т. д. Возможности безграничны (другие официальные документы могут найти более критические уровни).
Но, что интересно,это то, что ОС говорит вам: ЭЙ, ваше приложение ушло в фоновый режим!
Это именно то, что вы хотели узнать в первую очередь.
Как вы определяете, когда вы вернетесь?
Ну, это легко, я уверен, что у вас есть "BaseActivity", поэтому вы можете использовать onResume (), чтобы отметить тот факт, что вы вернулись.будет говорить, что вы не вернулись, когда вы фактически получите вызов к вышеуказанному методу onTrimMemory
.
Это работает. Вы не получаете ложных срабатываний. Если действие возобновляется, вы вернулись100% случаев.Если пользователь снова переходит на задний план, вы получаете еще один onTrimMemory()
звонок.
Вам необходимо указать свои действия (или, еще лучше, пользовательский класс).
Самый простой способ гарантироватьчто вы всегда получаете это, чтобы создать простой класс, подобный этому:
public class MemoryBoss implements ComponentCallbacks2 {
@Override
public void onConfigurationChanged(final Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// We're in the Background
}
// you might as well implement some memory cleanup here and be a nice Android dev.
}
}
Чтобы использовать это, в вашей реализации приложения ( у вас есть один, RIGHT? ), сделайте что-нибудьнапример:
MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMemoryBoss = new MemoryBoss();
registerComponentCallbacks(mMemoryBoss);
}
}
Если вы создадите Interface
, вы можете добавить else
к этому if
и реализовать ComponentCallbacks
(без 2), используемый во всем, что ниже API 14. Этот только обратный вызовимеет метод onLowMemory()
и не вызывается при переходе на фон , но вы должны использовать его для обрезки памяти.
Теперь запустите приложение и нажмите home.Ваш onTrimMemory(final int level)
метод должен быть вызван (подсказка: добавить ведение журнала).
Последний шаг - отменить регистрацию в обратном вызове.Вероятно, лучшим местом является метод onTerminate()
вашего приложения, , но , этот метод не вызывается на реальном устройстве:
/**
* This method is for use in emulated process environments. It will
* never be called on a production Android device, where processes are
* removed by simply killing them; no user code (including this callback)
* is executed when doing so.
*/
Такесли у вас действительно нет ситуации, когда вы больше не хотите регистрироваться, вы можете игнорировать ее, поскольку ваш процесс все равно умирает на уровне ОС.
Если вы решите отменить регистрацию в какой-то момент (если вы, дляНапример, предоставьте механизм выключения для вашего приложения, чтобы очистить и умереть), вы можете сделать:
unregisterComponentCallbacks(mMemoryBoss);
И это все.