Проверка, работает ли приложение Android в фоновом режиме - PullRequest
297 голосов
/ 08 сентября 2010

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

Ответы [ 29 ]

3 голосов
/ 13 февраля 2014

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

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

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

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

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

3 голосов
/ 29 января 2017

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

}
2 голосов
/ 23 января 2013

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

2 голосов
/ 08 сентября 2010

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

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

1 голос
/ 03 октября 2015

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

1 голос
/ 01 февраля 2018

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

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

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

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

1 голос
/ 07 сентября 2018

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

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

}
1 голос
/ 29 января 2018

Поскольку это еще не упомянуто, я предлагаю читателям изучить ProcessLifecycleOwner , доступный через компоненты архитектуры Android

0 голосов
/ 08 декабря 2014

См. Комментарий в функции 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 голосов
/ 30 января 2018

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

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

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

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


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