Проверка, работает ли приложение Android в фоновом режиме
Под фоном я подразумеваю, что ни одно из действий приложения в настоящее время не видимо пользователю?
38 ответов
Есть несколько способов определить, работает ли ваше приложение в фоновом режиме, но только один из них является полностью надежным:
Правильное решение (кредиты предоставляются Dan, CommonsWare и NeTeInStEiN)
Отслеживайте видимость своего приложения самостоятельно, используяActivity.onPause
,Activity.onResume
методы. Сохраните статус "видимости" в каком-то другом классе. Хороший выбор - ваша собственная реализацияApplication
илиService
(есть также несколько вариантов этого решения, если вы хотите проверить видимость активности из сервиса).
пример
Реализовать на заказApplication
класс (обратите внимание наisActivityVisible()
статический метод):public class MyApplication extends Application { public static boolean isActivityVisible() { return activityVisible; } public static void activityResumed() { activityVisible = true; } public static void activityPaused() { activityVisible = false; } private static boolean activityVisible; }
Зарегистрируйте свой класс приложения в
AndroidManifest.xml
:<application android:name="your.app.package.MyApplication" android:icon="@drawable/icon" android:label="@string/app_name" >
добавлять
onPause
а такжеonResume
каждомуActivity
в проекте (вы можете создать общего предка для своей деятельности, если хотите, но если ваша деятельность уже продлена сMapActivity
/ListActivity
и т.д. вам все еще нужно написать следующее от руки):@Override protected void onResume() { super.onResume(); MyApplication.activityResumed(); } @Override protected void onPause() { super.onPause(); MyApplication.activityPaused(); }
Обновить
ActivityLifecycleCallbacks были добавлены на уровне API 14 (Android 4.0). Вы можете использовать их, чтобы отслеживать, видна ли активность вашего приложения в данный момент для пользователя. Проверьте ответ Cornstalks ниже для деталей.Неправильный
Я имел обыкновение предлагать следующее решение:Вы можете обнаружить в настоящее время передний план / фоновое приложение с
ActivityManager.getRunningAppProcesses()
который возвращает списокRunningAppProcessInfo
записей. Чтобы определить, находится ли ваше приложение на переднем плане, проверьтеRunningAppProcessInfo.importance
поле для равенстваRunningAppProcessInfo.IMPORTANCE_FOREGROUND
в то время какRunningAppProcessInfo.processName
равно имени вашего пакета приложений.Также если вы позвоните
ActivityManager.getRunningAppProcesses()
из вашего пользовательского интерфейса потока он вернет важностьIMPORTANCE_FOREGROUND
для вашей задачи, независимо от того, на самом ли деле это на переднем плане или нет. Вызовите его в фоновом потоке (например, черезAsyncTask
) и он вернет правильные результаты.Хотя это решение может работать (и оно действительно работает большую часть времени), я настоятельно рекомендую воздержаться от его использования. И вот почему. Как написала Дайан Хэкборн:
Эти API-интерфейсы предназначены не для того, чтобы приложения основывали свой поток пользовательского интерфейса, а для того, чтобы показывать пользователю запущенные приложения, диспетчер задач или тому подобное.
Да, в памяти хранится список этих вещей. Тем не менее, он отключен в другом процессе, управляемом потоками, работающими отдельно от вас, и вы не можете рассчитывать на то, что (a) сможете вовремя увидеть правильное решение или (b) получить согласованную картину к тому времени, когда вы вернетесь. Кроме того, решение о том, к какому "следующему" действию идти, всегда принимается в момент, когда должно произойти переключение, и только в той точной точке (где состояние действия кратковременно блокируется, чтобы выполнить переключение), что мы на самом деле точно знаю, что будет дальше.
И реализация и глобальное поведение здесь не гарантируется, что останутся такими же в будущем.
Хотелось бы, чтобы я прочитал это до того, как опубликовал ответ на SO, но, надеюсь, еще не поздно признать мою ошибку.
Еще одно неправильное решение
Библиотека Droid-Fu, упомянутая в одном из ответов, используетActivityManager.getRunningTasks
для егоisApplicationBroughtToBackground
метод. Смотрите комментарий Дайанны выше и не используйте этот метод.
РЕШЕНИЕ GOOGLE - не хак, как предыдущие решения. Используйте ProcessLifecycleOwner
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
//App in background
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
// App in foreground
}
}
в приложении
dependencies {
...
implementation "android.arch.lifecycle:extensions:1.1.0"
}
allprojects {
repositories {
...
google()
jcenter()
maven { url 'https://maven.google.com' }
}
}
НЕ ИСПОЛЬЗУЙТЕ ЭТО ОТВЕТ
Ответ user1269737 - правильный (одобренный Google/Android) способ сделать это. Идите, прочитайте их ответ и дайте им +1.
Я оставлю свой оригинальный ответ здесь ради потомков. Это было лучшее из доступных в 2012 году, но теперь Android имеет соответствующую поддержку для этого.
Оригинальный ответ
Ключ использует ActivityLifecycleCallbacks
(обратите внимание, что для этого требуется Android API уровня 14 (Android 4.0)). Просто проверьте, равно ли количество остановленных действий количеству запущенных действий. Если они равны, ваша заявка в фоновом режиме. Если есть еще запущенные действия, ваше приложение все еще отображается. Если действия возобновлены, а не приостановлены, приложение не только отображается, но и на переднем плане. Существуют 3 основных состояния, в которых ваша деятельность может находиться, затем: видимая и на переднем плане, видимая, но не на переднем плане, и не видимая и не на переднем плане (то есть на заднем плане).
Отличная особенность этого метода в том, что у него нет асинхронных проблем getRunningTasks()
делает, но вы также не должны изменять каждый Activity
в вашем приложении, чтобы установить / сбросить что-то в onResumed()
/ onPaused()
, Это всего лишь несколько строк кода, которые самодостаточны и работают во всем приложении. Плюс, здесь нет никаких фанки-разрешений.
MyLifecycleHandler.java:
public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
// I use four separate variables here. You can, of course, just use two and
// increment/decrement them instead of using four and incrementing them all.
private int resumed;
private int paused;
private int started;
private int stopped;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
}
@Override
public void onActivityPaused(Activity activity) {
++paused;
android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
}
@Override
public void onActivityStopped(Activity activity) {
++stopped;
android.util.Log.w("test", "application is visible: " + (started > stopped));
}
// If you want a static function you can use to check if your application is
// foreground/background, you can use the following:
/*
// Replace the four variables above with these four
private static int resumed;
private static int paused;
private static int started;
private static int stopped;
// And these two public static functions
public static boolean isApplicationVisible() {
return started > stopped;
}
public static boolean isApplicationInForeground() {
return resumed > paused;
}
*/
}
MyApplication.java:
// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
@Override
public void onCreate() {
// Simply add the handler, and that's it! No need to add any code
// to every activity. Everything is contained in MyLifecycleHandler
// with just a few lines of code. Now *that's* nice.
registerActivityLifecycleCallbacks(new MyLifecycleHandler());
}
}
@Mewzer задал несколько хороших вопросов об этом методе, на которые я хотел бы ответить в этом ответе для всех:
onStop()
не вызывается в ситуациях с нехваткой памяти; это проблема здесь?
Нет. Документы для onStop()
сказать:
Обратите внимание, что этот метод никогда не может быть вызван в ситуациях нехватки памяти, когда системе не хватает памяти, чтобы поддерживать процесс вашей активности после вызова метода onPause().
Ключевым моментом здесь является "держать процесс вашей работы запущенным...". Если ситуация с нехваткой памяти когда-либо будет достигнута, ваш процесс фактически будет уничтожен (а не только ваша активность). Это означает, что этот метод проверки на наличие заземления все еще действителен, потому что а) вы не можете в любом случае проверять фоновую обработку, если ваш процесс убит, и б) если ваш процесс запускается снова (потому что создается новое действие), член переменные (статические или нет) для MyLifecycleHandler
будет сброшен в 0
,
Это работает для изменения конфигурации?
По умолчанию нет. Вы должны явно установить configChanges=orientation|screensize
(|
с чем-либо еще) в файле манифеста и обработайте изменения конфигурации, иначе ваша деятельность будет уничтожена и воссоздана. Если вы не установите это, методы вашей деятельности будут вызываться в следующем порядке: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume
, Как видите, перекрытия нет (как правило, при переключении между двумя действиями очень кратко перекрываются два действия, как работает этот метод обнаружения фона). Чтобы обойти это, вы должны установить configChanges
так что твоя деятельность не разрушена. К счастью, мне пришлось установить configChanges
уже во всех моих проектах, потому что было нежелательно, чтобы вся моя деятельность разрушалась при повороте / изменении размера экрана, поэтому я никогда не считал это проблематичным. (спасибо dpimka за то, что освежил мою память об этом и исправил меня!)
Одна запись:
Когда я сказал "фон" в этом ответе, я имел в виду "ваше приложение больше не видно". Действия Android могут быть видны, но не на переднем плане (например, если есть прозрачное наложение уведомлений). Вот почему я обновил этот ответ, чтобы отразить это.
Важно знать, что у Android есть странный подвешенный момент при переключении действий, когда ничто не находится на переднем плане. По этой причине, если вы проверяете, находится ли ваше приложение на переднем плане при переключении между действиями (в том же приложении), вам будет сказано, что вы не на переднем плане (даже если ваше приложение все еще является активным приложением и отображается).
Вы можете проверить, находится ли ваше приложение на переднем плане в вашем Activity
"s onPause()
метод после super.onPause()
, Просто вспомните странное состояние неопределенности, о котором я только что говорил.
Вы можете проверить, является ли ваше приложение видимым (то есть, если оно не в фоновом режиме) в вашем Activity
"s onStop()
метод после super.onStop()
,
Начиная с версии 26 библиотеки поддержки, вы можете использовать ProcessLifecycleOwner, просто добавьте его в свою зависимость, как описано здесь, например:
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData).
// Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}
А потом просто запрос ProcessLifecycleOwner
когда вы хотите для состояния приложения, примеры:
//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;
//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
Начиная с Android API 16 существует простой способ проверить, находится ли приложение на переднем плане. Это может быть небезопасно, но никакие методы на Android не являются надежными. Этот метод достаточно хорош для использования, когда ваша служба получает обновление от сервера и должна решить, показывать уведомление или нет (потому что, если пользовательский интерфейс находится на переднем плане, пользователь заметит обновление без уведомления).
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
Ответ Идолона подвержен ошибкам и гораздо более сложный, хотя повторять здесь проверить приложение для Android на переднем плане или нет? и здесь Определение текущего приоритетного приложения из фоновой задачи или службы
Существует гораздо более простой подход:
На BaseActivity, что все действия расширяются:
protected static boolean isVisible = false;
@Override
public void onResume()
{
super.onResume();
setVisible(true);
}
@Override
public void onPause()
{
super.onPause();
setVisible(false);
}
Всякий раз, когда вам нужно проверить, находится ли какое-либо из ваших действий приложения на переднем плане, просто проверьте isVisible()
;
Чтобы понять этот подход, проверьте ответ жизненного цикла параллельной деятельности: Жизненный цикл активности
Я попробовал рекомендованное решение, которое использует Application.ActivityLifecycleCallbacks и многие другие, но они не сработали, как ожидалось. Благодаря Sarge Borsch, я Sarge Borsch довольно простое и понятное решение, которое я описываю ниже.
Ключом к решению является факт понимания того, что если у нас есть ActivityA и ActivityB, и мы вызываем ActivityB из ActivityA (а не вызываем
ActivityA.finish
), затем ActivityB'sonStart()
будет вызван перед ActivityAonStop()
,
Это также основное различие между onStop()
а также onPause()
что никто не упомянул в статьях, которые я прочитал.
Таким образом, основываясь на поведении этого жизненного цикла, вы можете просто посчитать, сколько раз onStart()
а также onPause()
получил вызов в вашей программе. Обратите внимание, что для каждого Activity
вашей программы, вы должны переопределить onStart()
а также onStop()
, чтобы увеличить / уменьшить статическую переменную, используемую для подсчета. Ниже приведен код, реализующий эту логику. Обратите внимание, что я использую класс, который расширяет Application
, так что не забудьте объявить Manifest.xml
внутри тега приложения: android:name=".Utilities"
, хотя это может быть реализовано с использованием простого пользовательского класса.
public class Utilities extends Application
{
private static int stateCounter;
public void onCreate()
{
super.onCreate();
stateCounter = 0;
}
/**
* @return true if application is on background
* */
public static boolean isApplicationOnBackground()
{
return stateCounter == 0;
}
//to be called on each Activity onStart()
public static void activityStarted()
{
stateCounter++;
}
//to be called on each Activity onStop()
public static void activityStopped()
{
stateCounter--;
}
}
Теперь по каждому виду деятельности нашей программы мы должны переопределить onStart()
а также onStop()
и увеличивать / уменьшать, как показано ниже:
@Override
public void onStart()
{
super.onStart();
Utilities.activityStarted();
}
@Override
public void onStop()
{
Utilities.activityStopped();
if(Utilities.isApplicationOnBackground())
{
//you should want to check here if your application is on background
}
super.onStop();
}
С этой логикой, есть 2 возможных случая:
stateCounter = 0
: Количество остановленных равно количеству запущенных операций, что означает, что приложение работает в фоновом режиме.stateCounter > 0
: Количество запущенных больше, чем количество остановленных, что означает, что приложение работает на переднем плане.
Обратите внимание: stateCounter < 0
будет означать, что больше остановленных действий, чем начатых, что невозможно. Если вы столкнулись с этим случаем, это означает, что вы не увеличиваете / уменьшаете счетчик, как следует.
Вы готовы к работе. Вы должны проверить, находится ли ваше приложение на заднем плане внутри onStop()
,
Нет способа, если вы не отслеживаете это самостоятельно, чтобы определить, являются ли какие-либо из ваших действий видимыми или нет. Возможно, вам следует подумать о том, чтобы задать новый вопрос Stackru, объяснив, чего вы пытаетесь добиться от взаимодействия с пользователем, чтобы мы могли предложить вам альтернативные идеи реализации.
Вы можете использовать ComponentCallbacks2, чтобы определить, находится ли приложение в фоновом режиме. Кстати, этот обратный вызов доступен только в API уровня 14 (Ice Cream Sandwich) и выше.
Вы получите вызов метода:
public abstract void onTrimMemory (int level)
если уровень ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
тогда приложение находится в фоновом режиме.
Вы можете реализовать этот интерфейс для activity
, service
, так далее.
public class MainActivity extends AppCompatActivity 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) {
// app is in background
}
}
}
Опираясь на @Cornstalks ответ, чтобы включить пару полезных функций.
Дополнительные функции:
- представил одноэлементный шаблон, чтобы вы могли сделать это в любом месте приложения: AppLifecycleHandler.isApplicationVisible() и AppLifecycleHandler.isApplicationInForeground()
- добавлена обработка повторяющихся событий (см. комментарии // предпринять некоторые действия по изменению видимости и // предпринять некоторые действия по изменению на переднем плане)
App.java
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
}
}
AppLifecycleHandler.java
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private int resumed;
private int started;
private final String DebugName = "AppLifecycleHandler";
private boolean isVisible = false;
private boolean isInForeground = false;
private static AppLifecycleHandler instance;
public static AppLifecycleHandler getInstance() {
if (instance == null) {
instance = new AppLifecycleHandler();
}
return instance;
}
private AppLifecycleHandler() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivityPaused(Activity activity) {
--resumed;
android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
@Override
public void onActivityStopped(Activity activity) {
--started;
android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
private void setVisible(boolean visible) {
if (isVisible == visible) {
// no change
return;
}
// visibility changed
isVisible = visible;
android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);
// take some action on change of visibility
}
private void setForeground(boolean inForeground) {
if (isInForeground == inForeground) {
// no change
return;
}
// in foreground changed
isInForeground = inForeground;
android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);
// take some action on change of in foreground
}
public static boolean isApplicationVisible() {
return AppLifecycleHandler.getInstance().started > 0;
}
public static boolean isApplicationInForeground() {
return AppLifecycleHandler.getInstance().resumed > 0;
}
}
Единственное правильное решение:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
MyApp.java:
public class MyApp extends Application implements LifecycleObserver {
public static MainActivity mainActivity = null;
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onAppBackgrounded() {
// app in background
if (mainActivity != null) {
...
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppForegrounded() {
// app in foreground
if (mainActivity != null) {
...
}
}
}
Лучшее решение, которое я придумал, использует таймеры.
Вы запускаете таймер в onPause() и отменяете тот же таймер в onResume(), есть 1 экземпляр Timer (обычно определяется в классе Application). Сам таймер настроен на запуск Runnable через 2 секунды (или любой интервал, который вы считаете подходящим), когда таймер срабатывает, вы устанавливаете флаг, отмечающий приложение как фоновое.
В методе onResume () перед отменой таймера вы можете запросить флаг фона, чтобы выполнить любые операции запуска (например, начать загрузку или включить службы определения местоположения).
Это решение позволяет выполнять несколько операций в заднем стеке и не требует каких-либо разрешений для реализации.
Это решение хорошо работает, если вы также используете шину событий, поскольку ваш таймер может просто вызывать событие, и различные части вашего приложения могут реагировать соответствующим образом.
Если вы включите настройки разработчика "Не сохранять действия" - проверьте, что количество созданных активностей недостаточно. Вы должны проверить также isSaveInstanceState. Мой пользовательский метод isApplicationRunning() проверяет, запущено ли приложение для Android:
Вот мой рабочий код:
public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
private int created;
private boolean isSaveInstanceState;
private static AppLifecycleService instance;
private final static String TAG = AppLifecycleService.class.getName();
public static AppLifecycleService getInstance() {
if (instance == null) {
instance = new AppLifecycleService();
}
return instance;
}
public static boolean isApplicationRunning() {
boolean isApplicationRunning = true;
if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
isApplicationRunning = false;
}
return isApplicationRunning;
}
public static boolean isSaveInstanceState() {
return AppLifecycleService.getInstance().isSaveInstanceState;
}
public static int getCountCreatedActvities() {
return AppLifecycleService.getInstance().created;
}
private AppLifecycleService() {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
this.isSaveInstanceState = true;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
++created;
}
@Override
public void onActivityDestroyed(Activity activity) {
--created;
}
@Override
public void onActivityResumed(Activity activity) { }
@Override
public void onActivityPaused(Activity activity) { }
@Override
public void onActivityStarted(Activity activity) { }
@Override
public void onActivityStopped(Activity activity) { }
}
Я сделал свою собственную реализацию ActivityLifecycleCallbacks. Я использую SherlockActivity, но для обычного класса Activity может работать.
Во-первых, я создаю интерфейс, который имеет все методы для отслеживания жизненного цикла действий:
public interface ActivityLifecycleCallbacks{
public void onActivityStopped(Activity activity);
public void onActivityStarted(Activity activity);
public void onActivitySaveInstanceState(Activity activity, Bundle outState);
public void onActivityResumed(Activity activity);
public void onActivityPaused(Activity activity);
public void onActivityDestroyed(Activity activity);
public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}
Во-вторых, я реализовал этот интерфейс в классе моего приложения:
public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
}
@Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
@Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
}
@Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
}
}
В-третьих, я создаю класс, который выходит из SherlockActivity:
public class MySherlockActivity extends SherlockActivity {
protected MyApplication nMyApplication;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
nMyApplication = (MyApplication) getApplication();
nMyApplication.onActivityCreated(this, savedInstanceState);
}
protected void onResume() {
// TODO Auto-generated method stub
nMyApplication.onActivityResumed(this);
super.onResume();
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
nMyApplication.onActivityPaused(this);
super.onPause();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
nMyApplication.onActivityDestroyed(this);
super.onDestroy();
}
@Override
protected void onStart() {
nMyApplication.onActivityStarted(this);
super.onStart();
}
@Override
protected void onStop() {
nMyApplication.onActivityStopped(this);
super.onStop();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
nMyApplication.onActivitySaveInstanceState(this, outState);
super.onSaveInstanceState(outState);
}
}
В-четвертых, все классы, которые выходят из SherlockActivity, я заменил на MySherlockActivity:
public class MainActivity extends MySherlockActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Теперь в logcat вы увидите логи, запрограммированные в реализации интерфейса, выполненной в MyApplication.
Система различает фоновые и фоновые приложения. (Определение фона для целей ограничения услуг отличается от определения, используемого управлением памятью; приложение может находиться в фоновом режиме в том, что касается управления памятью, но на переднем плане - в отношении его способности запускать службы.) считается находящимся на переднем плане, если выполняется любое из следующих условий:
- У него есть видимое действие, независимо от того, запущено действие или приостановлено.
- У него есть сервис переднего плана.
- Другое приложение переднего плана подключается к приложению либо путем привязки к одной из его служб, либо с использованием одного из его поставщиков контента. Например, приложение находится на переднем плане, если к нему привязано другое приложение:
- IME
- Сервис обоев
- Слушатель уведомлений
- Голосовой или текстовый сервис
Если ни одно из этих условий не выполняется, приложение считается в фоновом режиме.
Никакое решение для меня не работает, однако я предлагаю необработанное решение. Это должно сработать. Если isAppBackground возвращает false, приложение должно быть на переднем плане.
public static boolean isAppBackground(Context context){
boolean isBackground=true;
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH){
List<ActivityManager.RunningAppProcessInfo> runningProcesses =activityManager.getRunningAppProcesses();
for(ActivityManager.RunningAppProcessInfo processInfo:runningProcesses){
if(processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
for(String activeProcess:processInfo.pkgList){
if(activeProcess.equals(context.getPackageName())){
isBackground = false;
}
}
}
}
}else{
List<ActivityManager.RunningTaskInfo> taskInfo = activityManager.getRunningTasks(1);
if(taskInfo.size()>0) {
ComponentName componentName = taskInfo.get(0).topActivity;
if(componentName.getPackageName().equals(context.getPackageName())){
isBackground = false;
}
}
}
return isBackground;
}
Чтобы прокомментировать сказанное CommonsWare и Key, вы, возможно, могли бы расширить класс Application и сделать так, чтобы все ваши действия вызывали его в своих методах onPause / onResume. Это позволит вам узнать, какие виды деятельности видны, но, вероятно, это можно сделать лучше.
Можете ли вы уточнить, что именно вы имеете в виду? Когда вы говорите, что работаете в фоновом режиме, вы подразумеваете, что ваше приложение все еще находится в памяти, хотя его нет на экране? Рассматривали ли вы использование Сервисов как более устойчивый способ управления вашим приложением, когда оно не в фокусе?
Простой и прямой ответ:
override fun onPause() {
Log.i("APP LIFECYCLE", "App Enter BACKground")
isForeground = false
super.onPause()
}
override fun onResume() {
Log.i("APP LIFECYCLE", "App Enter FOREground")
isForeground = true
super.onResume()
}
Тогда просто используйтеisForeground
свойство вашей деятельности, чтобы проверить статус.
Поскольку это еще не упомянуто, я предлагаю читателям изучить ProcessLifecycleOwner, доступный через компоненты архитектуры Android.
Я рекомендую прочитать эту страницу: http://developer.android.com/reference/android/app/Activity.html
Короче говоря, ваша активность больше не видна после onStop()
был вызван.
Активность приостанавливается, когда над ним появляется диалог, поэтому все рекомендуемые решения - это полу-решения. Вам также необходимо создать хуки для диалогов.
Этот код проверит
foreground
и
background
в любом состоянии:
Код Java:
private static boolean isApplicationForeground(Context context) {
KeyguardManager keyguardManager =
(KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager.isKeyguardLocked()) {
return false;
}
int myPid = Process.myPid();
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> list;
if ((list = activityManager.getRunningAppProcesses()) != null) {
for (ActivityManager.RunningAppProcessInfo aList : list) {
ActivityManager.RunningAppProcessInfo info;
if ((info = aList).pid == myPid) {
return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
}
}
return false;
}
Код Котлина:
private fun isApplicationForeground(context: Context): Boolean {
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
if (keyguardManager.isKeyguardLocked) {
return false
}
val myPid = Process.myPid()
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
var list: List<ActivityManager.RunningAppProcessInfo>
if (activityManager.runningAppProcesses.also { list = it } != null) {
for (aList in list) {
var info: ActivityManager.RunningAppProcessInfo
if (aList.also { info = it }.pid == myPid) {
return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
}
}
return false
}
Как насчет использования getApplicationState(). IsInForeground()?
Ни один из ответов не подходит для конкретного случая, если вы хотите узнать, выполняется ли конкретное действие на переднем плане и если вы являетесь SDK без прямого доступа к приложению. Для меня я был в фоновом потоке, только что получил push-уведомление о новом сообщении чата и хочу отображать системное уведомление только в том случае, если экран чата не находится на переднем плане.
Используя ActivityLifecycleCallbacks
что, как было рекомендовано в других ответах, я создал небольшой класс util, в котором содержится логика того, MyActivity
находится на переднем плане или нет.
class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {
private var isMyActivityInForeground = false
init {
(context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}
fun isMyActivityForeground() = isMyActivityInForeground
override fun onActivityPaused(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = false
}
}
override fun onActivityResumed(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = true
}
}
}
Я думаю, что этот вопрос должен быть более ясным. Когда? Куда? Какова ваша конкретная ситуация, которую вы хотите знать, если ваше приложение в фоновом режиме?
Я просто представляю свое решение по-своему.
Я получаю это с помощью поля "важность" RunningAppProcessInfo
класс в каждой деятельности onStop
метод в моем приложении, который может быть просто достигнут путем предоставления BaseActivity
для других мероприятий по расширению, которое реализует onStop
Способ проверки значения "важности". Вот код:
public static boolean isAppRunning(Context context) {
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(context.getPackageName())) {
if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
return true;
}
}
}
return false;
}
Другое решение для этого старого поста (для тех, которые могут помочь):
<application android:name=".BaseApplication" ... >
public class BaseApplication extends Application {
private class Status {
public boolean isVisible = true;
public boolean isFocused = true;
}
private Map<Activity, Status> activities;
@Override
public void onCreate() {
activities = new HashMap<Activity, Status>();
super.onCreate();
}
private boolean hasVisibleActivity() {
for (Status status : activities.values())
if (status.isVisible)
return true;
return false;
}
private boolean hasFocusedActivity() {
for (Status status : activities.values())
if (status.isFocused)
return true;
return false;
}
public void onActivityCreate(Activity activity, boolean isStarting) {
if (isStarting && activities.isEmpty())
onApplicationStart();
activities.put(activity, new Status());
}
public void onActivityStart(Activity activity) {
if (!hasVisibleActivity() && !hasFocusedActivity())
onApplicationForeground();
activities.get(activity).isVisible = true;
}
public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
activities.get(activity).isFocused = hasFocus;
}
public void onActivityStop(Activity activity, boolean isFinishing) {
activities.get(activity).isVisible = false;
if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
onApplicationBackground();
}
public void onActivityDestroy(Activity activity, boolean isFinishing) {
activities.remove(activity);
if(isFinishing && activities.isEmpty())
onApplicationStop();
}
private void onApplicationStart() {Log.i(null, "Start");}
private void onApplicationBackground() {Log.i(null, "Background");}
private void onApplicationForeground() {Log.i(null, "Foreground");}
private void onApplicationStop() {Log.i(null, "Stop");}
}
public class MyActivity extends BaseActivity {...}
public class BaseActivity extends Activity {
private BaseApplication application;
@Override
protected void onCreate(Bundle state) {
application = (BaseApplication) getApplication();
application.onActivityCreate(this, state == null);
super.onCreate(state);
}
@Override
protected void onStart() {
application.onActivityStart(this);
super.onStart();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
application.onActivityWindowFocusChanged(this, hasFocus);
super.onWindowFocusChanged(hasFocus);
}
@Override
protected void onStop() {
application.onActivityStop(this, isFinishing());
super.onStop();
}
@Override
protected void onDestroy() {
application.onActivityDestroy(this, isFinishing());
super.onDestroy();
}
}
По моему мнению, многие ответы вводят большую нагрузку кода и приносят много сложности и нечитаемости.
Когда люди спрашивают на SO, как общаться между Service
и Activity
Я обычно советую использовать LocalBroadcastManager.
Зачем?
Ну, цитируя документы:
Вы знаете, что передаваемые вами данные не покинут ваше приложение, поэтому вам не нужно беспокоиться о утечке личных данных.
Другие приложения не могут отправлять эти трансляции в ваше приложение, поэтому вам не нужно беспокоиться о наличии дыр в безопасности, которые они могут использовать.
Это более эффективно, чем отправка глобальной трансляции через систему.
Не в документах:
- Не требует внешних библиотек
- Код минимален
- Это быстро реализовать и понять
- Никаких пользовательских самореализованных обратных вызовов / ультра-синглтона / внутрипроцессного паттерна вообще...
- Нет сильных ссылок на
Activity
,Application
...
Описание
Итак, вы хотите проверить, если какой-либо из Activity
в настоящее время на переднем плане. Вы обычно делаете это в Service
или ваш Application
учебный класс.
Это значит, ваш Activity
объекты становятся отправителями сигнала (я включен / я выключен). Ваш Service
с другой стороны, становится Receiver
,
Есть два момента, в которых ваш Activity
говорит вам, если это происходит на переднем плане или на заднем плане (да только два... не 6).
Когда Activity
выходит на передний план, onResume()
метод запущен (также вызывается после onCreate()
).
Когда Activity
идет в спину, onPause()
называется.
Это моменты, в которые ваш Activity
должен отправить сигнал вашему Service
описать его состояние.
В случае нескольких Activity
х, помни Activity
сначала уходит на задний план, затем на передний план выходит еще один.
Так что ситуация будет такой:*
Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON
Service
/ Application
будет просто слушать эти сигналы и действовать соответственно.
Код (TLDR)
Ваш Service
должен реализовать BroadcastReceiver
чтобы слушать сигналы.
this.localBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// received data if Activity is on / off
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
Зарегистрировать Receiver
в Service::onCreate()
@Override
protected void onCreate() {
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
Отменить регистрацию в Service::onDestroy()
@Override
protected void onDestroy() {
// I'm dead, no need to listen to anything anymore.
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
Теперь ваш Activity
должны сообщить свое состояние.
В Activity::onResume()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
В Activity::onPause()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
Очень, очень распространенная ситуация
Разработчик: я хочу отправить данные из моего
Service
и обновитьActivity
, Как я могу проверить, еслиActivity
находится на переднем плане?
Обычно нет необходимости проверять, Activity
на переднем плане или нет. Просто отправьте данные через LocalBroadcastManager
от твоего Service
, Если Activity
включен, то он будет реагировать и действовать.
Для этой очень распространенной ситуации Service
становится отправителем, а Activity
реализует BroadcastReceiver
,
Итак, создайте Receiver
в вашем Activity
, Зарегистрируйте это в onResume()
и отмените регистрацию в onPause()
, Нет необходимости использовать другие методы жизненного цикла.
Определите Receiver
поведение в onReceive()
(обновить ListView, сделать это, сделать это, ...).
Таким образом, Activity
будет слушать, только если он находится на переднем плане, и ничего не произойдет, если он находится сзади или уничтожен.
В случае нескольких Activity
S, в зависимости от того, Activity
будет отвечать (если они также реализуют Receiver
).
Если все на заднем плане, никто не ответит, и сигнал просто потеряется.
Отправьте данные из Service
с помощью Intent
(см. код выше), указав идентификатор сигнала.
- За исключением поддержки нескольких окон. Это может быть сложно (пожалуйста, проверьте это при необходимости)...
Вот несколько способов
- Использование LifecycleEventObserver. Здесь ProcessLifecycleOwner обеспечивает жизненный цикл всего процесса приложения. Таким образом, onStateChanged вызывается, когда приложение переходит на передний/фоновый режим и не вызывается между переключениями активности.
import android.app.Application
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
class TestApplication: Application(), LifecycleEventObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this);
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Log.i("AppForegrounded", "Hello event ${event}")
if (event == Lifecycle.Event.ON_RESUME) {
isAppInForeground = true
} else {
isAppInForeground = false
}
}
companion object {
var isAppInForeground = false
}
}
а затем в Activity используйте его как
TestApplication.isAppInForeground
Обязательно укажите этот класс приложения в файле манифеста.
<manifest>
<application
android:name=".app.TestApplication"
</application>
</manifest>
Мы также можем сделать поле isAppInForeground наблюдаемым, чтобы мы могли прослушивать изменения вместо того, чтобы проверять, находится ли приложение на переднем плане, прежде чем выполнять какую-либо операцию.
- Использование DefaultLifecycleObserver. Здесь ProcessLifecycleOwner обеспечивает жизненный цикл всего процесса приложения. Таким образом, onResume или onPause вызывается, когда приложение переходит на передний/фоновый режим и не вызывается между переключениями активности.
class TestApplication: Application(), DefaultLifecycleObserver {
override fun onCreate() {
super<Application>.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this);
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
isAppInForeground = true
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
isAppInForeground = false
}
companion object {
var isAppInForeground = false
}
}
- Использование поля currentState ProcessLifecycleOwner
val isAppInForeground = ProcessLifecycleOwner.get().lifecycle.currentState == Lifecycle.State.RESUMED
val isAppInBackground = ProcessLifecycleOwner.get().lifecycle.currentState == Lifecycle.State.CREATED
fun isAppInForeground(): Boolean {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false
val appProcesses = activityManager.runningAppProcesses ?: return false
val packageName = packageName
for (appProcess in appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
return true
}
}
return false
}
Вы должны использовать общие предпочтения, чтобы хранить собственность и действовать в соответствии с ней, используя привязку службы из ваших действий. Если вы используете только привязку (то есть никогда не используете startService), то ваша служба будет работать только при привязке к ней (привязка onResume и unbind onPause), что заставит ее работать только на переднем плане, и если вы захотите работать с ней. В фоновом режиме вы можете использовать обычную услугу начала остановки.