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


329

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



7
Я запутался здесь .. почему Android не может обеспечить простое переопределение класса приложения для этого? Это слишком сложно знать на уровне платформы? @ Переопределить защищенный void onApplicationSentToBackground () {}
Чак Д,

2
@ChuckD - это имело бы смысл, и это то, что Android SDK, похоже, не хочет делать время от времени. : /
Mark


1
У iOS есть это в пиках, не уверен, почему Google делает это так сложно. Это такая очевидная необходимость.
Джерри Дестремпс

Ответы:


388

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

  1. Правильное решение (кредиты идут к Дэну , 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_FOREGROUNDwhile и RunningAppProcessInfo.processNameимя пакета вашего приложения.

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

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

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

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

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

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

  3. Другое неправильное решение, которое библиотека
    Droid-Fu упоминает в одном из ответов, использует ActivityManager.getRunningTasksдля своего isApplicationBroughtToBackgroundметода. Посмотрите комментарий Дайанны выше и тоже не используйте этот метод.


4
Чтобы узнать, нажали ли вы кнопку «Домой» или какое-либо другое приложение, вы получили фокус: 1) внедрите хорошее решение . 2) По OnStopзапросу isActivityVisible.
Брейс Габин

28
К сожалению, ваше «правильное» решение не работает для меня. Представьте, что вы переключаетесь между действиями в вашем приложении. Затем происходит то, что ваш флаг 'inForeground' выглядит следующим образом: True, False (между 1-й активностью onPause и 2-й активностью onResume), затем снова True и т. Д. Затем вам потребуется какой-то гистерезис.
Раду

14
Это решение не работает, если вы не можете напрямую контролировать все действия. Например, если у вас есть Activity от стороннего sdk или даже запущено намерение ACTION_VIEW.
user123321

66
Андроид такой долбаный крушение. Никто не думал, что кто-то может захотеть сохранить данные уровня приложения? Дай мне перерыв

8
Похоже, реальный ответ на этот вопрос «Вы не можете проверить это правильно». Так называемое «правильное» решение - в лучшем случае обходной путь, поэтому используется ActivityLifecycleCallbacks. Вам все еще нужно рассмотреть возможность переключения между действиями, которые будут зарегистрированы как "не на переднем плане". Меня поражает, что вы не можете проверить такие простые вещи ...
серин

263

НЕ ИСПОЛЬЗУЙТЕ ЭТО ОТВЕТ

Ответ 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() .


1
Это выглядит интересно - но что происходит в ситуациях с нехваткой памяти? Не гарантируется, что onStop () будет вызван. Можем ли мы когда-нибудь попасть в ситуацию, когда onStop () не вызывается и счетчик остановок не увеличивается - это означает, что фоновая проверка больше не является надежной? Или это никогда не произойдет?
Mewzer

1
Кроме того, это будет игнорировать изменения конфигурации? Или приложение будет считаться фоновым, если действие воссоздается в результате изменения конфигурации (например, изменение ориентации)? Извините, за вопросы, но я думаю, что вы на что-то и заинтересованы, чтобы знать, работает ли это в этих крайних случаях.
Mewzer

1
@Mewzer: Я собирался ответить в виде комментария, но для того, чтобы получить эти ответы, потребуется немного набрать текст, поэтому вернитесь через несколько минут, и я отредактирую свой ответ.
Cornstalks

1
@ Мьюзер: Вы должны найти свои ответы сейчас. Дайте мне знать, если есть еще вопросы!
Cornstalks

2
@Mewzer: Я только что добавил заметку, которая может вас заинтересовать. В частности, проверьте наличие фона onStop()после super.onStop(). Не проверяйте фоновый режим в onPause().
Cornstalks

187

РЕШЕНИЕ GOOGLE - не хак, как предыдущие решения. Используйте ProcessLifecycleOwner

Kotlin:

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
    }

}


Ява:

public class ArchLifecycleApp extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppForegrounded() {
        // App in foreground
    }
}

в приложении

dependencies {
    ...
    implementation "android.arch.lifecycle:extensions:1.1.0"

    //New Android X dependency is this - 
    implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

}

allprojects {
    repositories {
        ...
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Вы можете прочитать больше о компонентах архитектуры, связанных с жизненным циклом, здесь - https://developer.android.com/topic/libraries/architecture/lifecycle


10
Это определенно должен быть правильный ответ! Это работало как очарование: D
JaviOverflow

2
Это работает отлично, я также немного изменил, чтобы мне было легче получить доступ к переднему плану / состоянию фона вне этого класса: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }тогда вы можете получить состояние переднего плана сArchLifecycleApp.isForeground()
Jose Jet

2
О, чувак, это намного лучше, чем мой старый ответ. +1 от меня. Я обновил свой ответ, чтобы указать людям на ваш.
Кукурузные початки

2
Хотя это правильный ответ, нет необходимости реализовывать обратные вызовы, вы можете просто запросить ProcessLifecycleOwner, когда захотите. Проверьте stackoverflow.com/a/52678290/6600000
Кейван Эсбати

2
Как говорит док The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , это не работает для multiple processesприложений, есть ли какой-нибудь API, который мы можем достичь элегантно?
Acntwww

23

Начиная с версии 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);

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

если приложение будет полностью закрыто, что вернет первый метод?
Евгений Мишустин

@EvgeniyMishustin, который зависит от текущего состояния приложения, но обычно вы видите CREATED, а затем DESTROYED, и после этого вы не будете получать никаких новых событий.
Кейван Эсбати

Так где же какое-либо утверждение "IF", чтобы увидеть, если приложение находится в фоновом режиме (на переднем плане) ???
ekashking

@ekashking просто поместил весь оператор в условие if. Например: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => Приложение находится на переднем плане
Кейван Эсбати,

20

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

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

должен ли этот код находиться внутри класса Service или другого класса, например, класса Application? Большое спасибо.
Woppi

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

Это та же методика, что и в AWS Android SDK для push-уведомлений.
spakmad

Имейте в виду, что «определение фона для целей ограничения услуг отличается от определения, используемого управлением памятью; приложение может находиться в фоновом режиме в том, что касается управления памятью, но на переднем плане - в отношении его способности запускать службы».) " developer.android.com/about/versions/oreo/background.html (
ARLabs

Спасибо, это сработало! Я смог использовать этот код в, JobServiceчтобы обнаружить, что служба работает в фоновом режиме.
Майкл Ософски

17

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

Существует гораздо более простой подход:

На BaseActivity, что все действия расширяются:

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }

Всякий раз, когда вам нужно проверить, находится ли какое-либо из ваших действий приложения на переднем плане, просто проверьте isVisible() ;

Чтобы понять этот подход, проверьте ответ «жизненный цикл параллельной деятельности»: жизненный цикл активности


3
Idolon's answer is error prone- к сожалению, я должен согласиться с вами. На основании комментария Дайан Хэкборн в группах Google я обновил свой ответ. Проверьте это, пожалуйста, для деталей.
Идолон

2
Это тоже не надежное решение. Один из сценариев не является ли пользователь разобрали панель уведомлений, а затем ни onPause, onStop, ни onResumeсобытие называется. Так что же делать, если ни одно из этих событий не запущено ?!

Это привело меня к этому вопросу: stackoverflow.com/questions/33657102/…
Ruchir Baronia

К сожалению, этот код работает неправильно, когда действие запускается, когда экран выключен. В этом случае onResume и onPause вызываются, создавая isVisible = false.
CoolMind

@CoolMind Не могли бы вы объяснить, каков вариант использования, когда вы запускаете действие в фоновом режиме?
neteinstein

11

Я попытался рекомендовать решение, которое использует Application.ActivityLifecycleCallbacks и многие другие, но они не сработали, как ожидалось. Благодаря Sarge я нашел довольно простое и понятное решение, которое я описываю ниже.

Ключом решения является тот факт, что если у нас есть ActivityA и ActivityB, и мы вызываем ActivityB из ActivityA (а не вызываем ActivityA.finish), то ActivityB onStart()будет вызываться перед ActivityA onStop().

Это также основная разница между onStop()и onPause()что никто не сделал упоминания в статьях , которые я прочитал.

Таким образом, основываясь на поведении этого жизненного цикла, вы можете просто посчитать, сколько раз это делали onStart()и onPause()вызывали в вашей программе. Обратите внимание, что для каждой Activity вашей программы вы должны переопределить onStart()и onStop(), чтобы увеличить / уменьшить статическую переменную, используемую для подсчета. Ниже приведен код, реализующий эту логику. Обратите внимание, что я использую класс, который расширяется Application, поэтому не забудьте объявить Manifest.xmlвнутри тега Application: 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().


Я бы переехал if(Utilities.isApplicationOnBackground()) …в Utilities. Потому что в противном случае только конкретное действие будет реагировать на событие.
Отображаемое имя

10

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


2
В андроиде у нас есть настройка под названием «Фоновые данные». Этот параметр отключает любое фоновое соединение данных, когда приложение работает в фоновом режиме. Я хочу реализовать переключатель «Фоновые данные» для своего приложения, поэтому, когда ни одно из моих действий не будет видно пользователю, я бы хотел, чтобы моя служба прекратила передачу данных, но в тот момент, когда возобновится одно из моих действий, я бы хотел, чтобы возобновить передачу данных
cppdev

1
@cppdev: Будем надеяться, что «передача данных» проводится по Service. Если это так, попросите службу уведомить службу по мере их появления и исчезновения. Если Serviceвыясняется, что никаких видимых действий нет, и он остается таким в течение некоторого времени, остановите передачу данных в следующей точке логического останова. Да, для этого потребуется код для каждого из ваших действий, но сейчас это неизбежно, AFAIK.
CommonsWare

1
Если вы хотите избежать копирования и вставки общего кода между всеми вашими действиями, вы можете создать класс, MyActivityClassнаследующий Activityи реализующий методы жизненного цикла, и сделать все ваши действия наследуемыми MyActivityClass. Это не будет работать для PreferenceActivityили MapActivityхотя (см. Этот вопрос )
Гийом Brunerie

@ CommonsWare Я пытался с OnPause () OnResume (), чтобы он был активен или нет, но если мое приложение не отображается на экране просмотра, если оно работает в фоновом режиме, как проверить, активно оно или нет
Manoj

@ CommonsWare Я пытался с OnPause () OnResume (), что оно активно или нет, но если мое приложение не отображается на экране просмотра, если оно работает в фоновом режиме, как проверить, активно ли оно или нет
Manoj

5

Вы можете использовать ComponentCallbacks2, чтобы определить, находится ли приложение в фоновом режиме. Кстати, этот обратный вызов доступен только в API Level 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
     }
   }
}

1
Опробовал ваш ответ, но не так надежно. обратный вызов onTrimMemory не сработает, когда экран заблокирован, а также при нажатии кнопки «Питание» для блокировки экрана. Также не всегда будет возвращаться TRIM_MEMORY_UI_HIDDEN, если ваше приложение видно и вы открываете другое приложение через уведомление в строке состояния. Единственное надежное решение - реализовать ActivityLifecycleCallbacks и настроить его в соответствии с вариантом использования.
velval

4

Опираясь на @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;
    }
}

3

Лучшее решение, которое я придумал, использует таймеры.

Вы запускаете таймер в onPause () и отменяете тот же таймер в onResume (), есть 1 экземпляр Timer (обычно определяется в классе Application). Сам таймер настроен на запуск Runnable через 2 секунды (или любой интервал, который вы считаете подходящим), когда срабатывает таймер, вы устанавливаете флаг, отмечающий приложение как фоновое.

В методе onResume () перед отменой таймера можно запросить флаг фона для выполнения любых операций запуска (например, начать загрузку или включить службы определения местоположения).

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

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


Я начинаю думать, что это лучшее (хотя и неудачное) решение
dhaag23

Да, это лучшее решение, которое мне удалось. Мне нужно было остановить сканирование по Bluetooth, когда приложение не было предопределено, но я не мог просто использовать onpause, остановку или уничтожение, потому что я не хотел постоянно останавливаться и запускаться, когда пользователь перемещался по приложению.
CaptRespect

3

Если вы включите настройки разработчика «Не сохранять активности» - проверьте, что количество созданных активностей недостаточно. Вы должны проверить также 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) { }        

}

3

Единственное правильное решение:

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) {
            ...
        }
    }

}

Я не понимаю, как это решение может дать мне ответ на простой вопрос в заявлении IF о моей активности (или фрагменте), находится ли мое приложение на заднем плане или на переднем плане. Где заявление "ЕСЛИ" ???
ekashking

2

Чтобы прокомментировать сказанное в CommonsWare и Key, вы можете расширить класс Application и сделать так, чтобы все ваши действия вызывали его в своих методах onPause / onResume. Это позволит вам узнать, какие виды деятельности видны, но, вероятно, это можно сделать лучше.

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


В андроиде у нас есть настройка под названием «Фоновые данные». Этот параметр отключает любое фоновое соединение данных, когда приложение работает в фоновом режиме. Я хочу реализовать переключатель «Фоновые данные» для своего приложения, поэтому, когда ни одно из моих действий не будет видно пользователю, я бы хотел, чтобы мой сервис прекратил выполнять какую-либо передачу данных, но в момент возобновления одного из моих действий я хотел бы возобновить передачу данных
cppdev

1
Applicationне имеет onPause()или onResume().
CommonsWare

1
@CommonsWare Вы правы, я имел в виду каждую отдельную активность, связываясь с приложением по их паузе / резюме. По сути, это идея, которую вы только что поделились в комментарии к своему ответу, хотя вы использовали Сервисы, и я думаю, это более разумный шаг.
Дэн

2

Я сделал свою собственную реализацию 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

Активность приостанавливается, когда над ним появляется диалог, поэтому все рекомендуемые решения являются полурешениями. Вам также необходимо создать хуки для диалогов.



1

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

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

  1. У него есть видимое действие, независимо от того, запущено или приостановлено действие.
  2. У него есть сервис переднего плана.
  3. Другое приложение переднего плана подключается к приложению либо путем привязки к одной из его служб, либо с использованием одного из его поставщиков контента. Например, приложение находится на переднем плане, если к нему привязано другое приложение:
    • IME
    • Сервис обоев
    • Слушатель уведомлений
    • Голосовой или текстовый сервис

Если ни одно из этих условий не выполняется, приложение считается в фоновом режиме.


0

Другое решение для этого старого поста (для тех, которые могут помочь):


<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();
    }

}

0

Смотрите комментарий в функции onActivityDestroyed.

Работает с целевой версией SDK 14>:

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @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());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}

0

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


0

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

Я просто представляю свое решение по-своему.
Для этого я использую поле «важность» 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;
}

Это не рекомендуемое решение, как указано в ответе @ Idolon.
CoolMind

0

Я рекомендую прочитать эту страницу: http://developer.android.com/reference/android/app/Activity.html.

Короче говоря, ваша активность больше не видна после onStop()вызова.


3
У меня есть около 10 мероприятий в моей заявке. Поэтому я хочу знать, если ни один из них, если видим для пользователя. В общем, я хочу знать, работает ли мое приложение в целом в фоновом режиме
cppdev

Итак, вы отслеживаете все 10-ти. Или, как предложил CommonsWare, объясните, что вы пытаетесь сделать.
Ключ

3
Это не правильно. Ваша активность видна до onStop; между onPauseи onStopэто видно , но не на переднем плане .
Никгрим

@nickgrim: что не правильно? Я заявил, что после вызова вызывается активность onStop(), которая соответствует тому, что вы написали.
Ключ

@Key: Вы первоначально сказали, пока onPauseне называется: недавнее редактирование исправило вас.
Никгрим


0

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

Когда люди спрашивают на SO, как общаться между a Serviceи a 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, Activityответ будет зависеть (если они также реализуютReceiver ).

Если все на заднем плане, никто не ответит, и сигнал просто потеряется.

Отправьте данные Serviceчерез канал Intent(см. Код выше), указав идентификатор сигнала.


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

0
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
}

0

Ни один из ответов не вполне соответствовал конкретному случаю, если вы выяснили, находится ли конкретная активность в forground и если вы SDK без прямого доступа к Приложению. Для меня я был в фоновом потоке, только что получил push-уведомление о новом сообщении чата и хочу отображать системное уведомление, только если экран чата не находится на переднем плане.

Используя это ActivityLifecycleCallbacks, как было рекомендовано в других ответах, я создал небольшой класс утилит, в котором содержится логика того, 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
    }
}

}


-1

В моих действиях onResume и onPause я записываю логическое значение isVisible для SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

И читайте это в другом месте, когда это необходимо,

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

Может быть, не элегантно, но это работает для меня ...


-1

Возможно, будет слишком поздно, чтобы ответить, но если кто-то приходит в гости, то вот решение, которое я предлагаю: причина (ы), по которой приложение хочет знать, что он находится в фоновом режиме или выходит на передний план, может быть много, некоторые из них 1. Показывать тосты и уведомления, когда пользователь находится в BG. 2. Для выполнения некоторых задач пользователь впервые приходит из BG, например, опрос, перерисовка и т. Д.

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

Я сделал что-то, что было предложено CommonsWare: «Если Служба определяет, что никаких видимых действий нет, и она остается такой в ​​течение некоторого времени , остановите передачу данных на следующей логической остановке».

Строка, выделенная жирным шрифтом, важна, и ее можно использовать для достижения второго пункта. Так что я делаю, как только я получаю onActivityPaused (), я не изменяю видимое на false напрямую, вместо этого имею таймер в 3 секунды (то есть максимум, что должно быть запущено следующее действие), и если нет onActivityResumed ( ) в течение следующих 3 секунд измените видимость на ложь. Аналогично в onActivityResumed (), если есть таймер, я отменяю его. Подводя итог, видимым становится isAppInBackground.

Извините не могу скопировать и вставить код ...


-3

Я хотел бы рекомендовать вам использовать другой способ сделать это.

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.