Проверка, работает ли приложение Android в фоновом режиме

Под фоном я подразумеваю, что ни одно из действий приложения в настоящее время не видимо пользователю?

38 ответов

Есть несколько способов определить, работает ли ваше приложение в фоновом режиме, но только один из них является полностью надежным:

  1. Правильное решение (кредиты предоставляются 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 ниже для деталей.

  2. Неправильный
    Я имел обыкновение предлагать следующее решение:

    Вы можете обнаружить в настоящее время передний план / фоновое приложение с ActivityManager.getRunningAppProcesses() который возвращает список RunningAppProcessInfo записей. Чтобы определить, находится ли ваше приложение на переднем плане, проверьте RunningAppProcessInfo.importance поле для равенства RunningAppProcessInfo.IMPORTANCE_FOREGROUND в то время как RunningAppProcessInfo.processName равно имени вашего пакета приложений.

    Также если вы позвоните ActivityManager.getRunningAppProcesses() из вашего пользовательского интерфейса потока он вернет важность IMPORTANCE_FOREGROUND для вашей задачи, независимо от того, на самом ли деле это на переднем плане или нет. Вызовите его в фоновом потоке (например, через AsyncTask) и он вернет правильные результаты.

    Хотя это решение может работать (и оно действительно работает большую часть времени), я настоятельно рекомендую воздержаться от его использования. И вот почему. Как написала Дайан Хэкборн:

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

    Да, в памяти хранится список этих вещей. Тем не менее, он отключен в другом процессе, управляемом потоками, работающими отдельно от вас, и вы не можете рассчитывать на то, что (a) сможете вовремя увидеть правильное решение или (b) получить согласованную картину к тому времени, когда вы вернетесь. Кроме того, решение о том, к какому "следующему" действию идти, всегда принимается в момент, когда должно произойти переключение, и только в той точной точке (где состояние действия кратковременно блокируется, чтобы выполнить переключение), что мы на самом деле точно знаю, что будет дальше.

    И реализация и глобальное поведение здесь не гарантируется, что останутся такими же в будущем.

    Хотелось бы, чтобы я прочитал это до того, как опубликовал ответ на SO, но, надеюсь, еще не поздно признать мою ошибку.

  3. Еще одно неправильное решение
    Библиотека 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's onStart() будет вызван перед ActivityA onStop(),

Это также основное различие между 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 возможных случая:

  1. stateCounter = 0: Количество остановленных равно количеству запущенных операций, что означает, что приложение работает в фоновом режиме.
  2. 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.

Официальные документы:

Система различает фоновые и фоновые приложения. (Определение фона для целей ограничения услуг отличается от определения, используемого управлением памятью; приложение может находиться в фоновом режиме в том, что касается управления памятью, но на переднем плане - в отношении его способности запускать службы.) считается находящимся на переднем плане, если выполняется любое из следующих условий:

  1. У него есть видимое действие, независимо от того, запущено действие или приостановлено.
  2. У него есть сервис переднего плана.
  3. Другое приложение переднего плана подключается к приложению либо путем привязки к одной из его служб, либо с использованием одного из его поставщиков контента. Например, приложение находится на переднем плане, если к нему привязано другое приложение:
    • 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 (см. код выше), указав идентификатор сигнала.


  • За исключением поддержки нескольких окон. Это может быть сложно (пожалуйста, проверьте это при необходимости)...

Вот несколько способов

  1. Использование 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 наблюдаемым, чтобы мы могли прослушивать изменения вместо того, чтобы проверять, находится ли приложение на переднем плане, прежде чем выполнять какую-либо операцию.

  1. Использование 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
    }
}
  1. Использование поля 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), что заставит ее работать только на переднем плане, и если вы захотите работать с ней. В фоновом режиме вы можете использовать обычную услугу начала остановки.

Другие вопросы по тегам