Как узнать, работает ли Android-приложение на переднем плане?

Я делаю уведомление в строке состояния в своем приложении для Android, которое запускается c2dm. Я не хочу показывать уведомление, если приложение работает. Как определить, работает ли приложение и находится ли он на переднем плане?

Ответ 1

Сделайте глобальную переменную типа private boolean mIsInForegroundMode; и назначьте значение false в onPause() и значение true в onResume().

Пример кода:

private boolean mIsInForegroundMode;

@Override
protected void onPause() {
    super.onPause();
    mIsInForegroundMode = false;
}

@Override
protected void onResume() {
    super.onResume();
    mIsInForegroundMode = true;
}

// Some function.
public boolean isInForeground() {
    return mIsInForegroundMode;
}

Ответ 2

В качестве альтернативы вы можете проверить с помощью ActivityManager, какие задачи выполняются методом getRunningTasks. Затем проверьте с первой задачей (задача на переднем плане) в возвращенном списке задач, если это ваша задача.
Вот пример кода:

public Notification buildNotification(String arg0, Map<String, String> arg1) {

    ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> services = activityManager
            .getRunningTasks(Integer.MAX_VALUE);
    boolean isActivityFound = false;

    if (services.get(0).topActivity.getPackageName().toString()
            .equalsIgnoreCase(appContext.getPackageName().toString())) {
        isActivityFound = true;
    }

    if (isActivityFound) {
        return null;
    } else {
        // write your code to build a notification.
        // return the notification you built here
    }

}

И не забудьте добавить разрешение GET_TASKS в файл manifest.xml, чтобы иметь возможность запускать метод getRunningTasks() в приведенном выше коде:

<uses-permission android:name="android.permission.GET_TASKS" />

p/s: Если вы согласны с этим, обратите внимание, что это разрешение теперь устарело.

Ответ 3

Это довольно старый пост, но по-прежнему весьма актуальный. Вышеприведенное решение может работать, но не так. Как сообщила Диана Хакборн:

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

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

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

Правильное решение - реализовать: ActivityLifeCycleCallbacks.

Для этого в основном требуется класс приложения, и в нем может быть установлен обработчик для определения состояния ваших действий в приложении.

Ответ 4

Как говорит Vinay, вероятно, лучшим решением (для поддержки более новых версий Android 14+) является использование ActivityLifecycleCallbacks в реализации класса Application.

package com.telcel.contenedor.appdelegate;

import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;

/** Determines global app lifecycle states. 
 * 
 * The following is the reference of activities states:
 * 
 * The <b>visible</b> lifetime of an activity happens between a call to onStart()
 * until a corresponding call to onStop(). During this time the user can see the
 * activity on-screen, though it may not be in the foreground and interacting with 
 * the user. The onStart() and onStop() methods can be called multiple times, as 
 * the activity becomes visible and hidden to the user.
 * 
 * The <b>foreground</b> lifetime of an activity happens between a call to onResume()
 * until a corresponding call to onPause(). During this time the activity is in front
 * of all other activities and interacting with the user. An activity can frequently
 * go between the resumed and paused states -- for example when the device goes to
 * sleep, when an activity result is delivered, when a new intent is delivered -- 
 * so the code in these methods should be fairly lightweight. 
 * 
 * */
public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks {

    /** Manages the state of opened vs closed activities, should be 0 or 1. 
     * It will be 2 if this value is checked between activity B onStart() and
     * activity A onStop().
     * It could be greater if the top activities are not fullscreen or have
     * transparent backgrounds.
     */
    private static int visibleActivityCount = 0;

    /** Manages the state of opened vs closed activities, should be 0 or 1
     * because only one can be in foreground at a time. It will be 2 if this 
     * value is checked between activity B onResume() and activity A onPause().
     */
    private static int foregroundActivityCount = 0;

    /** Returns true if app has foreground */
    public static boolean isAppInForeground(){
        return foregroundActivityCount > 0;
    }

    /** Returns true if any activity of app is visible (or device is sleep when
     * an activity was visible) */
    public static boolean isAppVisible(){
        return visibleActivityCount > 0;
    }

    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    public void onActivityDestroyed(Activity activity) {
    }

    public void onActivityResumed(Activity activity) {
        foregroundActivityCount ++;
    }

    public void onActivityPaused(Activity activity) {
        foregroundActivityCount --;
    }


    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    public void onActivityStarted(Activity activity) {
        visibleActivityCount ++;
    }

    public void onActivityStopped(Activity activity) {
        visibleActivityCount --;
    }
}

И в приложении onCreate():

registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());

Тогда ApplicationLifecycleManager.isAppVisible() или ApplicationLifecycleManager.isAppInForeground() будет использоваться для определения желаемого состояния.

Ответ 5

FYI, если вы используете решение Gadenkan (это здорово!!), не забудьте добавить

<uses-permission android:name="android.permission.GET_TASKS" />

к манифесту.

Ответ 6

Немного очищенная версия решения Gadenkan. Поместите это любое действие или, возможно, базовый класс для всех ваших действий.

protected boolean isRunningInForeground() {
    ActivityManager manager = 
         (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
    if (tasks.isEmpty()) {
        return false;
    }
    String topActivityName = tasks.get(0).topActivity.getPackageName();
    return topActivityName.equalsIgnoreCase(getPackageName());
}

Чтобы позвонить getRunningTasks(), вам нужно добавить это в свой AndroidManifest.xml:

<uses-permission android:name="android.permission.GET_TASKS"/>

Заметьте, что ActivityManager.getRunningTasks() Javadoc говорит, хотя:

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

Обновление (февраль 2015 г.)

Обратите внимание, что getRunningTasks() был устарел на уровне API 21!

По состоянию LOLLIPOP этот метод больше не доступен для сторонних приложений: введение рецентов, ориентированных на документ, означает, что он может информация для вызывающего абонента. Для обратной совместимости все равно будет вернуть небольшое подмножество своих данных: по крайней мере, собственные задачи вызывающего абонента, и, возможно, некоторые другие задачи, такие как дома, которые, как известно, не являются чувствительным.

Итак, то, что я написал ранее, еще более актуально:

Во многих случаях вы, вероятно, можете найти лучшее решение. Например, что-то делать в onPause() и onResume(), возможно, в BaseActivity для всех ваших действий.

(В нашем случае мы не хотели запускать оффлайн-предупреждение, если мы не находимся на переднем плане, поэтому в BaseActivity onPause() мы просто отменим подписку на RxJava Subscription на прослушивание сигнала "off offline". )

Ответ 7

С API 16 вы можете сделать это следующим образом:

static boolean shouldShowNotification(Context context) {
    RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
    ActivityManager.getMyMemoryState(myProcess);
    if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
        return true;

    KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
    // app is in foreground, but if screen is locked show notification anyway
    return km.inKeyguardRestrictedInputMode();
}

Ответ 8

В ответ на ответ Gadenkan мне нужно было что-то вроде этого, чтобы я мог определить, не работает ли мое приложение на переднем плане, но мне нужно было что-то, что было достаточно для приложения, и не требовало, чтобы я устанавливал/снимал флаги во всем приложении.

Код Gadenkan в значительной степени поразил гвоздь на голове, но он не был в моем собственном стиле и чувствовал, что он может быть более аккуратным, поэтому в моем приложении его сгустили до этого.

if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}

(Боковое примечание: вы можете просто удалить!, если вы хотите, чтобы проверка работала наоборот)

Хотя при таком подходе вам нужно разрешение GET_TASKS.

Ответ 9

Вот метод, который я использую (и поддерживающий метод):

private boolean checkIfAppIsRunningInForeground() {
    ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) {
        if(appProcessInfo.processName.contains(this.getPackageName())) {
            return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance);
        }
    }
    return false;
}

private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) {
    switch (appImportance) {
        //user is aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE:
            return true;
        //user is not aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE:
        default:
            return false;
    }
}

Ответ 10

Я хотел бы добавить, что более безопасный способ сделать это - чем проверить, находится ли ваше приложение в фоновом режиме перед созданием уведомления, - это просто отключить и включить широковещательный приемник onPause() и onResume() соответственно.

Этот метод дает вам больше контроля в реальной логике приложения и вряд ли изменится в будущем.

@Override
protected void onPause() {
    unregisterReceiver(mHandleMessageReceiver);
    super.onPause();
}

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}

Ответ 11

Я нашел более простой и точный способ проверить, находится ли приложение на переднем плане или в фоновом режиме, сопоставляя действия с булевыми.

Проверьте полный gist здесь

Ответ 12

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

Вместо использования Менеджера активности есть простой трюк, который вы можете сделать с помощью кода. Если вы внимательно наблюдаете за циклом активности, поток между двумя действиями и передним фоном выглядит следующим образом. Предположим, что A и B являются двумя действиями.

При переходе от A к B:   1. onPause() от A называется   2. onResume() of B называется   3. onStop() of A вызывается, когда B полностью возобновляется

Когда приложение переходит в фоновый режим:   1. onPause() от A называется   2. onStop() алгебры A называется

Вы можете обнаружить фоновое событие, просто поместив флаг в действие.

Сделайте абстрактное действие и расширьте его из других действий, так что вам не придется копировать код для всех других действий, где вам нужно фоновое событие.

В абстрактной активности создайте флаг isAppInBackground.

В методе onCreate(): isAppInBackground = false;

В методе onPause(): isAppInBackground = false;

В методе onStop(): isAppInBackground = true;

Вам просто нужно проверить свой onResume(), если isAppInBackground - это правда. n после того, как вы проверите свой флаг, затем снова установите isAppInBackground = false

Для перехода между двумя действиями, так как onSTop() первой всегда будет вызываться после возобновления второй активности, флаг никогда не будет правдой, а когда приложение находится в фоновом режиме, onStop() активности вызывается сразу после onPause, и, следовательно, флаг будет Верно, когда вы откроете приложение позже.

В этом подходе есть еще один сценарий. Если какой-либо экран вашего приложения уже открыт, и вы поместили мобильный простоя, то через некоторое время мобильный телефон перейдет в спящий режим, и когда вы разблокируете мобильный телефон, он будет обработан при фоновом событии.

Ответ 13

Основываясь на различных ответах и ​​комментариях, вот более инлайн-версия, которую вы можете добавить к вспомогательному классу:

public static boolean isAppInForeground(Context context) {
  List<RunningTaskInfo> task =
      ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
          .getRunningTasks(1);
  if (task.isEmpty()) {
    return false;
  }
  return task
      .get(0)
      .topActivity
      .getPackageName()
      .equalsIgnoreCase(context.getPackageName());
}

Как уже упоминалось в других ответах, вам нужно добавить следующие разрешения для AndroidManifest.xml.

<uses-permission android:name="android.permission.GET_TASKS"/>

Ответ 14

Здесь приведен код для простого простого решения, описанного выше пользователем @user2690455. Хотя это выглядит немного подробным, вы увидите, что на самом деле он довольно легкий

В моем случае мы также используем AppCompatActivity, поэтому мне пришлось иметь 2 базовых класса.

public class BaseActivity extends Activity {

    /**
     * Let field be set only in base class
     * All callers must use accessors,
     * and then it not up to them to manage state.
     *
     * Making it static since ..
     * 1. It needs to be used across two base classes
     * 2. It a singleton state in the app
     */
    private static boolean IS_APP_IN_BACKGROUND = false;

    @Override
    protected void onResume() {
        super.onResume();

        BaseActivity.onResumeAppTracking(this);

        BaseActivity.setAppInBackgroundFalse();
    }

    @Override
    protected void onStop() {
        super.onStop();

        BaseActivity.setAppInBackgroundTrue();
    }

    @Override
    protected void onPause() {
        super.onPause();

        BaseActivity.setAppInBackgroundFalse();
    }

    protected static void onResumeAppTracking(Activity activity) {

        if (BaseActivity.isAppInBackground()) {

            // do requirements for returning app to foreground
        }

    }

    protected static void setAppInBackgroundFalse() {

        IS_APP_IN_BACKGROUND = false;
    }

    protected static void setAppInBackgroundTrue() {

        IS_APP_IN_BACKGROUND = true;
    }

    protected static boolean isAppInBackground() {

        return IS_APP_IN_BACKGROUND;
    }
}

Ответ 15

Для этого нет глобального обратного вызова, но для каждого действия это onStop(). Вам не нужно возиться с атомом int. Просто введите глобальный int с количеством запущенных действий, в каждом действии добавьте его в onStart() и уменьшите его на onStop().

Следуйте этим

Ответ 16

     public static boolean isAppRunning(Context context) {

 // check with the first task(task in the foreground)
 // in the returned list of tasks

   ActivityManager activityManager = (ActivityManager)
   context.getSystemService(Context.ACTIVITY_SERVICE);
 List<RunningTaskInfo> services =
 activityManager.getRunningTasks(Integer.MAX_VALUE);
     if
     (services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
     {
     return true;
     }
     return false;
     }

Ответ 17

Предыдущие подходы, упомянутые здесь, не являются оптимальными. Для подхода, основанного на задаче, требуется разрешение, которое может быть нежелательным, и подход "Boolean" склонен к одновременным изменениям беспорядков.

Подход, который я использую, и который (я считаю) работает довольно хорошо в большинстве случаев:

У вас есть класс "MainApplication" для отслеживания количества операций в AtomicInteger:

import android.app.Application;

import java.util.concurrent.atomic.AtomicInteger;

public class MainApplication extends Application {
    static class ActivityCounter {
        private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0);

        public static boolean isAppActive() {
            return ACTIVITY_COUNT.get() > 0;
        }

        public static void activityStarted() {
            ACTIVITY_COUNT.incrementAndGet();
        }

        public static void activityStopped() {
            ACTIVITY_COUNT.decrementAndGet();
        }
    }
}

И создайте базовый класс активности, который продлит другие действия:

import android.app.Activity;
import android.support.annotation.CallSuper;

public class TestActivity extends Activity {
    @Override
    @CallSuper
    protected void onStart() {
        MainApplication.ActivityCounter.activityStarted();
        super.onStart();
    }

    @Override
    @CallSuper
    protected void onStop() {
        MainApplication.ActivityCounter.activityStopped();
        super.onStop();
    }
}