Я пытаюсь написать приложение, которое делает что-то конкретное, когда через некоторое время оно возвращается на передний план. Есть ли способ определить, когда приложение отправляется в фоновый режим или выводится на передний план?
android
background
foreground
iHorse
источник
источник
Ответы:
onPause()
ИonResume()
методы вызываются , когда приложение доводится до фона и на первый план снова. Однако они также вызываются при первом запуске приложения и до его закрытия. Вы можете прочитать больше в деятельности .Не существует прямого подхода к получению статуса приложения в фоновом режиме или на переднем плане, но даже я столкнулся с этой проблемой и нашел решение с помощью
onWindowFocusChanged
иonStop
.Более подробную информацию можно найти здесь : Android: решение для определения, когда приложение Android переходит в фоновый режим и возвращается на передний план без getRunningTasks или getRunningAppProcesses .
источник
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
(вам нужно будет создать один, если у вас его нет).Я рекомендую onStart, потому что диалоги могут приостанавливать действие, поэтому держу пари, что вы не хотите, чтобы ваше приложение считало «оно ушло на задний план», если все, что вы делали, это отображали полноэкранное диалоговое окно, но ваш пробег может отличаться.
И это все. Код в блоке if будет выполнен только один раз , даже если вы перейдете к другому действию, новый (который также
extends BaseActivity
) сообщит оwasInBackground
том,false
что он не будет выполнять код, пока неonMemoryTrimmed
будет вызван и флаг снова не будет установлен в true ,Надеюсь, это поможет.
ОБНОВЛЕНИЕ / ЗАМЕТКИ (апрель 2015 г.) : Прежде чем перейти к копированию и вставке этого кода, обратите внимание, что я обнаружил пару случаев, когда он может быть ненадежным на 100% и должен сочетаться с другими методами для достижения наилучших результатов. В частности, есть два известных случая, когда
onTrimMemory
обратный вызов не гарантированно будет выполнен:Если ваш телефон блокирует экран, когда ваше приложение видно (скажем, ваше устройство блокируется через nn минут), этот обратный вызов не вызывается (или не всегда), потому что экран блокировки находится сверху, но ваше приложение все еще «работает», хотя и закрыто.
Если у вашего устройства относительно мало памяти (и он испытывает недостаток памяти), операционная система, похоже, игнорирует этот вызов и сразу переходит к более критическим уровням.
Теперь, в зависимости от того, насколько важно для вас знать, когда ваше приложение перешло в фоновый режим, вам может понадобиться, а может и не понадобиться расширять это решение вместе с отслеживанием жизненного цикла активности и так далее.
Просто имейте это в виду и получите хорошую команду по тестированию;)
КОНЕЦ ОБНОВЛЕНИЯ
Это может быть поздно, но есть надежный метод в Ice Cream Sandwich (API 14) и выше .
Оказывается, что когда ваше приложение не имеет больше видимого интерфейса, вызывается обратный вызов. Обратный вызов, который вы можете реализовать в пользовательском классе, называется ComponentCallbacks2 (да, с двумя). Этот обратный вызов доступен только в API уровня 14 (Ice Cream Sandwich) и выше.
Вы в основном получаете вызов метода:
Уровень 20 или более конкретно
Я тестировал это, и оно всегда работает, потому что уровень 20 - это просто «предложение», что вы можете захотеть освободить некоторые ресурсы, так как ваше приложение больше не видно.
Цитировать официальные документы:
Конечно, вы должны реализовать это, чтобы на самом деле делать то, что он говорит (очистить память, которая не использовалась в определенное время, очистить некоторые коллекции, которые оставались неиспользованными, и т. Д. Возможности безграничны (другие официальные документы см. Для более подробной информации). критические уровни).
Но, что интересно, ОС говорит вам: ЭЙ, ваше приложение ушло на второй план!
Что именно то, что вы хотели знать в первую очередь.
Как вы определяете, когда вернулись?
Ну, это легко, я уверен, что у вас есть «BaseActivity», так что вы можете использовать onResume (), чтобы отметить тот факт, что вы вернулись. Потому что единственный раз, когда вы будете говорить, что вы не вернулись, это когда вы на самом деле получаете вызов
onTrimMemory
методу.Оно работает. Вы не получаете ложных срабатываний. Если действие возобновляется, вы возвращаетесь в 100% случаев. Если пользователь снова идет назад, вы получаете другой
onTrimMemory()
звонок.Вы должны подписать ваши действия (или еще лучше, пользовательский класс).
Самый простой способ гарантировать, что вы всегда получите это, - создать простой класс, подобный этому:
Чтобы использовать это, в вашей реализации приложения (у вас есть одна, RIGHT? ), Сделайте что-то вроде:
Если вы создадите объект,
Interface
вы можете добавитьelse
к немуif
и реализоватьComponentCallbacks
(без 2), который используется в API ниже 14. Этот обратный вызов имеет толькоonLowMemory()
метод и не вызывается при переходе в фоновый режим. , но вы должны использовать его для обрезки памяти. ,Теперь запустите приложение и нажмите «Домой». Ваш
onTrimMemory(final int level)
метод должен быть вызван (подсказка: добавить ведение журнала).Последний шаг - отменить регистрацию в обратном вызове. Вероятно, лучшим местом является
onTerminate()
метод вашего приложения, но этот метод не вызывается на реальном устройстве:Поэтому, если у вас действительно нет ситуации, когда вы больше не хотите регистрироваться, вы можете игнорировать ее, поскольку ваш процесс в любом случае умирает на уровне ОС.
Если в какой-то момент вы решите отменить регистрацию (например, если вы предоставите механизм завершения работы своего приложения для очистки и смерти), вы можете сделать следующее:
И это все.
источник
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
метод, который позволяет избежать проблемы в вашем обновлении, пункт 2. Что касается пункта 1, то меня это не беспокоит, поскольку приложение на самом деле не ушло в фоновый режим, поэтому оно должно работать.Вот как мне удалось это решить. Он работает исходя из того, что использование временной привязки между переходами активности, скорее всего, предоставит достаточные доказательства того, что приложение было «фоновым» или нет.
Во-первых, я использовал экземпляр android.app.Application (назовем его MyApplication), у которого есть Timer, TimerTask, константа, представляющая максимальное количество миллисекунд, которое разумно может занять переход от одного действия к другому (я пошел со значением 2s) и логическим значением, указывающим, было ли приложение «в фоновом режиме»:
Приложение также предоставляет два метода для запуска и остановки таймера / задачи:
Последняя часть этого решения - добавить вызов к каждому из этих методов из событий onResume () и onPause () всех действий или, предпочтительно, в базовом действии, от которого наследуются все ваши конкретные действия:
Таким образом, в случае, когда пользователь просто перемещается между действиями вашего приложения, onPause () уходящего действия запускает таймер, но почти сразу вводимое новое действие отменяет таймер, прежде чем оно достигнет максимального времени перехода. И так было InBackground было бы ложным .
С другой стороны, когда активность выходит на передний план из панели запуска, пробуждения устройства, завершения телефонного звонка и т. Д., Более вероятно, задача таймера, выполненная до этого события, и, таким образом, для wasInBackground было задано значение true .
источник
Изменить: новые компоненты архитектуры принесли что-то многообещающее: ProcessLifecycleOwner , см . Ответ @ vokilam
Фактическое решение в соответствии с докладом Google I / O :
Да. Я знаю, трудно поверить, что это простое решение работает, поскольку у нас здесь так много странных решений.
Но есть надежда.
источник
ProcessLifecycleOwner
кажется многообещающим решением также.Реализация может быть настолько простой, как
Согласно исходному коду текущее значение задержки равно
700ms
.Также использование этой функции требует
dependencies
:источник
implementation "android.arch.lifecycle:extensions:1.0.0"
иannotationProcessor "android.arch.lifecycle:compiler:1.0.0"
из репозитория Google (то естьgoogle()
)Основываясь на ответе Мартина Марконцини (спасибо!), Я наконец нашел надежное (и очень простое) решение.
Затем добавьте это в ваш onCreate () вашего класса приложения
источник
Мы используем этот метод. Это выглядит слишком просто для работы, но оно было хорошо протестировано в нашем приложении и фактически работает на удивление хорошо во всех случаях, включая переход на домашний экран с помощью кнопки «домой», с помощью кнопки «возврат» или после блокировки экрана. Попробуйте.
Идея в том, что на переднем плане Android всегда начинает новую активность перед тем, как остановить предыдущую. Это не гарантировано, но вот как это работает. Кстати, Flurry, похоже, использует ту же логику (просто предположение, я этого не проверял, но перехватывает те же события).
Изменить: согласно комментариям, мы также перешли к onStart () в более поздних версиях кода. Кроме того, я добавляю супер звонки, которые отсутствовали в моем первоначальном посте, потому что это была скорее концепция, чем рабочий код.
источник
onStop is called when the activity is no longer visible to the user
.Если ваше приложение состоит из нескольких активностей и / или сложенных активностей, таких как виджет панели вкладок, то переопределение onPause () и onResume () не будет работать. Т.е. при запуске нового действия текущие действия будут приостановлены перед созданием нового. То же самое относится и к завершению (с помощью кнопки «назад») действия.
Я нашел два метода, которые, кажется, работают так, как хотели.
Первый требует разрешения GET_TASKS и состоит из простого метода, который проверяет, относится ли наиболее активная активность на устройстве к приложению, сравнивая имена пакетов:
Этот метод был найден в структуре Droid-Fu (теперь называется Ignition).
Второй метод, который я реализовал самостоятельно, не требует разрешения GET_TASKS, что хорошо. Вместо этого это немного сложнее в реализации.
В вашем классе MainApplication у вас есть переменная, которая отслеживает количество запущенных действий в вашем приложении. В onResume () для каждого действия вы увеличиваете переменную, а в onPause () вы уменьшаете ее.
Когда количество запущенных операций достигает 0, приложение переводится в фоновый режим, если выполняются следующие условия:
Когда вы можете обнаружить, что приложение перешло в фоновый режим, легко обнаружить, когда оно возвращается на передний план.
источник
getRunnintTasks()
Создайте класс, который расширяется
Application
. Тогда в нем мы можем использовать его метод переопределения,onTrimMemory()
.Чтобы определить, перешло ли приложение в фоновый режим, мы будем использовать:
источник
FragmentActivity
вы тоже можете захотеть добавитьlevel == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
тоже.Попробуйте использовать onUserLeaveHint. Это будет вызываться только тогда, когда ваше приложение переходит в фоновый режим. onPause будет иметь дело с угловыми случаями, так как он может быть вызван по другим причинам; например, если пользователь открывает другое действие в вашем приложении, например страницу настроек, будет вызываться метод onPause вашего основного действия, даже если они все еще находятся в вашем приложении; отслеживание происходящего приведет к ошибкам, когда вместо этого вы можете просто использовать обратный вызов onUserLeaveHint, который выполняет то, что вы запрашиваете.
Когда вызывается UserLeaveHint, вы можете установить для логического флага inBackground значение true. Когда вызывается onResume, только предполагайте, что вы вернулись на передний план, если установлен флаг inBackground. Это потому, что onResume также будет вызываться в вашей основной деятельности, если пользователь был только в вашем меню настроек и никогда не выходил из приложения.
Помните, что если пользователь нажимает кнопку «Домой» на экране настроек, onUserLeaveHint будет вызываться в ваших действиях с настройками, а когда они возвращаются, onResume будет вызываться в ваших действиях с настройками. Если у вас есть только этот код обнаружения в вашей основной деятельности, вы пропустите этот вариант использования. Чтобы этот код был во всех ваших действиях без дублирования кода, создайте абстрактный класс действий, расширяющий Activity, и вставьте в него свой общий код. Тогда каждое ваше занятие может расширять это абстрактное занятие.
Например:
источник
ActivityLifecycleCallbacks может быть интересен, но он недостаточно хорошо документирован.
Однако, если вы вызываете registerActivityLifecycleCallbacks (), вы сможете получить обратные вызовы для случаев, когда действия создаются, уничтожаются и т. Д. Вы можете вызвать getComponentName () для действия.
источник
android.arch.lifecycle предоставляет классы и интерфейсы, позволяющие создавать компоненты с учетом жизненного цикла.
Ваше приложение должно реализовать интерфейс LifecycleObserver:
Для этого вам нужно добавить эту зависимость в ваш файл build.gradle:
В соответствии с рекомендациями Google вы должны минимизировать код, выполняемый в методах действий жизненного цикла:
Вы можете прочитать больше здесь: https://developer.android.com/topic/libraries/architecture/lifecycle
источник
В вашем Приложении добавьте обратный вызов и проверьте активность root следующим образом:
источник
Я создал проект на Github app-foreground-background-listen
Создайте BaseActivity для всех действий в вашем приложении.
Теперь используйте эту BaseActivity в качестве суперкласса всей вашей Activity, например MainActivity расширяет BaseActivity, и onAppStart будет вызываться при запуске приложения, а onAppPause () - при переходе приложения в фоновый режим с любого экрана.
источник
Это довольно легко с ProcessLifecycleOwner
Добавьте эти зависимости
В Котлине :
Тогда в вашей основной деятельности:
Смотрите мою статью по этой теме: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48
источник
Вы можете использовать ProcessLifecycleOwner, прикрепляя к нему наблюдателя жизненного цикла.
затем в
onCreate()
вашем классе Application вы вызываете это:с этим вы сможете захватить события
ON_PAUSE
иON_STOP
вашего приложения , которые происходят , когда речь идет в фоновом режиме.источник
Не существует простых методов жизненного цикла, которые бы указывали, когда все приложение переходит в фоновый режим.
Я сделал это простым способом. Следуйте приведенным ниже инструкциям для определения фона приложения / фазы переднего плана.
С небольшим обходным путем, это возможно. Здесь на помощь приходит ActivityLifecycleCallbacks . Позвольте мне пройти через шаг за шагом.
Сначала создайте класс, который расширяет приложение android.app.Application и реализует интерфейс ActivityLifecycleCallbacks . В Application.onCreate () зарегистрируйте обратный вызов.
Зарегистрируйте класс «App» в манифесте, как показано ниже
<application android:name=".App"
.Когда приложение находится на переднем плане, будет по крайней мере одна активность в начальном состоянии, а когда приложение находится в фоновом режиме, активности не будет.
Объявите 2 переменные, как показано ниже в классе «App».
activityReferences
будет вести подсчет количества действий в запущенном состоянии.isActivityChangingConfigurations
флаг, указывающий, проходит ли текущее действие изменение конфигурации, например, переключатель ориентации.Используя следующий код, вы можете определить, выходит ли приложение на передний план.
Это как определить, работает ли приложение в фоновом режиме.
Как это работает:
Это небольшая хитрость, связанная с тем, как методы жизненного цикла вызываются последовательно. Позвольте мне пройти сценарий.
Предположим, что пользователь запускает приложение и запускает Launcher Activity A. Жизненный цикл звонков будет,
Теперь действие A начинает действие B.
Затем пользователь переходит обратно из действия B,
Затем пользователь нажимает кнопку «Домой»,
В случае, если пользователь нажимает кнопку «Домой» в «Деятельности B» вместо кнопки «Назад», он все равно останется прежним, и ActivityReferences будет
0
. Следовательно, мы можем определить, как приложение входит в фоновый режим.Итак, какова роль
isActivityChangingConfigurations
? В приведенном выше сценарии предположим, что действие B меняет ориентацию. Последовательность обратного вызова будет,Вот почему у нас есть дополнительная проверка,
isActivityChangingConfigurations
чтобы избежать сценария, когда действие проходит через изменения конфигурации.источник
Я нашел хороший метод для определения приложения, будь то ввод переднего плана или фона. Вот мой код . Надеюсь, это поможет вам.
}
источник
Ты можешь использовать:
Различаться между новыми запусками и перезапусками.
источник
Редактировать 2: То, что я написал ниже, на самом деле не будет работать. Google отклонил приложение, которое включает вызов ActivityManager.getRunningTasks (). Из документации видно, что этот API предназначен только для целей отладки и разработки. Я буду обновлять этот пост, как только у меня будет время обновить проект GitHub, приведенный ниже, с новой схемой, которая использует таймеры и почти так же хороша.
Изменить 1: я написал сообщение в блоге и создал простой репозиторий GitHub, чтобы сделать это действительно легко.
Принятый и получивший самый высокий рейтинг ответ - не самый лучший подход. Реализация метода isApplicationBroughtToBackground () с наивысшим рейтингом не обрабатывает ситуацию, когда основное действие приложения уступает действию, определенному в том же приложении, но имеет другой пакет Java. Я придумал способ сделать это, который будет работать в этом случае.
Вызовите это в onPause (), и он сообщит вам, уходит ли ваше приложение в фоновый режим, потому что другое приложение запущено или пользователь нажал кнопку «Домой».
источник
Правильный ответ здесь
Создайте класс с именем MyApp, как показано ниже:
Затем везде, где вы хотите (лучше сначала запустить приложение в приложении), добавьте код ниже:
Выполнено! Теперь, когда приложение находится в фоновом режиме, мы получаем журнал,
status : we are out
и когда мы заходим в приложение, мы получаем журналstatus : we are out
источник
Мое решение было вдохновлено ответом @ d60402 и также опирается на временное окно, но не использует
Timer
:где
SingletonApplication
расширениеApplication
класса:источник
Я использовал это с Google Analytics EasyTracker, и это сработало. Это может быть расширено, чтобы делать то, что вы ищете, используя простое целое число.
источник
я знаю, что немного поздно, но я думаю, что у всех этих ответов есть некоторые проблемы, в то время как я сделал это как ниже, и это работает отлично.
создайте обратный вызов жизненного цикла действия следующим образом:
и просто зарегистрируйте его в своем классе приложения, как показано ниже:
источник
Похоже, это один из самых сложных вопросов в Android, поскольку (на момент написания статьи) Android не имеет эквивалентов iOS
applicationDidEnterBackground()
илиapplicationWillEnterForeground()
обратных вызовов. Я использовал библиотеку AppState, которая была собрана @jenzz .Оказалось, что это именно то, что мне нужно, особенно потому, что в моем приложении было несколько действий, поэтому простая проверка
onStart()
илиonStop()
действие не собиралась его сокращать.Сначала я добавил эти зависимости в gradle:
Тогда было просто добавить эти строки в соответствующее место в вашем коде:
В зависимости от того, как вы подписываетесь на наблюдаемое, вам, возможно, придется отказаться от него, чтобы избежать утечек памяти. Опять больше информации на странице GitHub .
источник
Это измененная версия ответа @ d60402: https://stackoverflow.com/a/15573121/4747587
Делай все что там упомянуто. Но вместо того, чтобы иметь
Base Activity
и делать это как родитель для каждого действия и переопределенияonResume()
иonPause
, сделайте следующее:В своем классе приложения добавьте строку:
registerActivityLifecycleCallbacks (обратный вызов Application.ActivityLifecycleCallbacks);
Здесь
callback
есть все методы жизненного цикла активности, и теперь вы можете переопределитьonActivityResumed()
иonActivityPaused()
.Взгляните на этот Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
источник
Вы можете достичь этого легко с помощью
ActivityLifecycleCallbacks
иComponentCallbacks2
что-то вроде ниже.Создайте класс,
AppLifeCycleHandler
реализующий вышеупомянутые интерфейсы.В вашем классе, который расширяет
Application
реализацию,AppLifeCycleCallback
чтобы получить обратные вызовы, когда приложение переключается между передним и задним планом. Что-то вроде ниже.Надеюсь это поможет.
РЕДАКТИРОВАТЬ В качестве альтернативы теперь вы можете использовать компонент архитектуры с учетом жизненного цикла.
источник
Поскольку я не нашел никакого подхода, который также обрабатывает вращение без проверки меток времени, я подумал, что я также поделюсь тем, как мы сейчас делаем это в нашем приложении. Единственное дополнение к этому ответу https://stackoverflow.com/a/42679191/5119746 заключается в том, что мы также принимаем во внимание ориентацию.
Затем для обратных вызовов у нас сначала есть резюме:
И на ActiveStopped:
И затем, вот прибавление: Проверка на изменения ориентации:
Вот и все. Надеюсь, это поможет кому-то :)
источник
Мы можем расширить это решение, используя
LiveData
:Теперь мы можем подписаться на эту LiveData и ловить нужные события. Например:
источник
Эти ответы не кажутся правильными. Эти методы также вызываются, когда начинается и заканчивается другое действие. Что вы можете сделать, так это сохранить глобальный флаг (да, глобальные переменные плохие :) и установить его в true каждый раз, когда вы начинаете новое действие. Установите значение false в onCreate каждого действия. Затем в onPause вы проверяете этот флаг. Если оно ложно, ваше приложение уходит в фоновый режим или его убивают.
источник