Как узнать, работает ли приложение Android на переднем плане? - PullRequest
81 голосов
/ 31 марта 2011

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

Ответы [ 18 ]

111 голосов
/ 14 июня 2012

Кроме того, вы можете проверить с помощью 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: Если вы согласны с этим, обратите внимание, что это разрешение устарело.

53 голосов
/ 31 марта 2011

Создайте глобальную переменную типа 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;
}
46 голосов
/ 30 октября 2014

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

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

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

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

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

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

23 голосов
/ 14 января 2016

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

17 голосов
/ 20 сентября 2016

С 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();
}
15 голосов
/ 18 декабря 2012

К вашему сведению, если вы используете решение Гаденкана (что здорово !!), не забудьте добавить

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

к манифесту.

14 голосов
/ 20 ноября 2013

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

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, прослушивающего сигнал «перешел в автономный режим». )

9 голосов
/ 12 сентября 2012

После ответа Гаденкана мне нужно было что-то вроде этого, чтобы я мог сказать, не было ли мое приложение запущенным на переднем плане, но мне нужно что-то, что было бы широким для приложения и не требовало от меня установки / сброса флагов во всем приложении.

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

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

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

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

2 голосов
/ 07 августа 2015

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

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

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

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 21 марта 2015

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

Проверьте всю суть здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...