Как определить, когда приложение переходит в фоновый режим и вернуться на переднем плане

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

30 ответов


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

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

подробности здесь Android: решение для обнаружения, когда Android приложение уходит в фоновом режиме и вернуться на передний план без getRunningTasks или getRunningAppProcesses.


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

во-первых, я использовал android.приложение.Экземпляр приложения (назовем его MyApplication), который имеет таймер, TimerTask, константу для представления максимального количества миллисекунд, которое может разумно занять переход от одного действия к другому (я пошел с значение 2s) и логическое значение, указывающее, было ли приложение "в фоновом режиме":

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

приложение также предоставляет два метода для запуска и остановки таймера/задачи:

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

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

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

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

Так в случае, когда пользователь просто перемещается между действиями вашего приложения, onPause () уходящей активности запускает таймер, но почти сразу же вводится новое действие отменяет таймер, прежде чем он сможет достичь максимального времени перехода. И так wasInBackground будет false.

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


2018: Android поддерживает Это изначально через компоненты жизненного цикла.

обновление марта 2018: теперь есть лучшее решение. См.ProcessLifecycleOwner. Вам нужно будет использовать новые компоненты архитектуры 1.1.0 (последние на данный момент), но это конкретно предназначен для этого.

есть простой образец при условии в ответ но я написал образец приложение и a блоге об этом.

С тех пор как я написал это еще в 2014 году, возникли различные решения. Некоторые работали, некоторые были думал, что работает, но имел недостатки (включая мои!) и мы, как сообщество (Android), научились жить с последствиями и писали обходные пути для особых случаев.

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

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

если нет веской причины для вас не использовать новые компоненты архитектуры (и есть некоторые, особенно если вы нацелены на супер старые API), то продолжайте использовать их. Они далеки от совершенства, но ни один из них не был ComponentCallbacks2.

обновление / заметки (ноябрь 2015): люди делали два комментарии, во-первых, это >= вместо == потому что в документации говорится, что вы не следует проверять точные значения. Это хорошо для большинства случаев, но имейте в виду, что если вы только о том,что-то когда приложение пошло в фоновом режиме, вам придется использовать == и также объедините его с другим решением (например, обратные вызовы жизненного цикла активности), или вы не может сделать нужный эффект. Пример (и это случилось со мной), что если вы хотите замок ваше приложение с экраном пароля, когда он идет в фоновом режиме (например, 1Password, если вы знакомы с ним), вы можете случайно заблокировать приложение, если у вас мало памяти и вдруг тестирование для >= TRIM_MEMORY, потому что Android вызовет LOW MEMORY позвоните, и это выше, чем у вас. Поэтому будьте осторожны, как / что вы тестируете.

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

самый простой способ, который я могу придумать, объясняется ниже, но так как некоторые люди незнакомы с ним, я добавляю некоторый псевдо-код прямо здесь. Если у вас есть YourApplication и MemoryBoss классов, в своем class BaseActivity extends Activity (вам нужно будет создать один, если вы не один).

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

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

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

и это все. Код в блоке if будет только один раз, даже если вы переходите к другому виду деятельности, новому (это также extends BaseActivity) будет отчет wasInBackground и false поэтому он не будет выполнять код, до onMemoryTrimmed вызывается, и флаг снова устанавливается в true.

надеюсь, это поможет.

UPDATE / NOTES (апрель 2015): прежде чем вы пойдете все копировать и вставлять этот код, обратите внимание что я нашел пару случаев, когда он не может быть на 100% надежным и должны быть объединены с другими методами для достижения наилучших результатов. Примечательно, что есть два известных экземпляров здесь onTrimMemory обратный вызов не гарантируется для выполнения:

  1. если ваш телефон блокирует экран, пока ваше приложение видно (скажем, ваше устройство блокируется после nn минут), этот обратный вызов не вызывается (или не всегда), потому что экран блокировки просто включен сверху, но ваше приложение все еще "работает", хотя и покрыто.

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

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

просто имейте в виду вышесказанное и иметь хорошую команду QA;)

КОНЕЦ ОБНОВЛЕНИЯ

может быть поздно, но есть надежный метод в сэндвич с мороженым (API 14) и выше.

оказывается, что когда ваше приложение больше не имеет видимого пользовательского интерфейса, запускается обратный вызов. Обратный вызов, который можно реализовать в пользовательском классе, называется ComponentCallbacks2 (да, с двумя). Этот обратный вызов доступно только на уровне API 14 (сэндвич с мороженым) и выше.

вы в основном получаете вызов метода:

public abstract void onTrimMemory (int level)

Уровень 20 или более конкретно

public static final int TRIM_MEMORY_UI_HIDDEN

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

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


Edit: новые компоненты архитектуры принесли что-то многообещающее:ProcessLifecycleOwner см. @vokilam это


фактическое решение согласно Google I/O talk:

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

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

но есть надежда.


на основе ответа Мартина Марконсиниса (спасибо!) Я, наконец, нашел надежное (и очень простое) решение.

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

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

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

затем добавьте это в свой onCreate () вашего класса приложения

public class MyApp extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}

ProcessLifecycleOwner кажется, также является многообещающим решением.

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

реализация может быть такой же простой, как

public class AppLifecycleListener implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onMoveToForeground() {
        // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onMoveToBackground() {
       // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleListener());

согласно исходному коду, текущее значение задержки 700ms.


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

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

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

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


Если ваше приложение состоит из нескольких активностей и/или штабелированных активностей, таких как виджет панели вкладок, переопределение onPause() и onResume() не будет работать. Т. е. при запуске нового действия текущие действия будут приостановлены до создания нового. То же самое относится и к завершению (с помощью кнопки "Назад") действия.

Я нашел два метода, которые, похоже, работают так, как хотели.

первый требует разрешения GET_TASKS и состоит из простого метода, который проверяет, принадлежит ли верхняя запущенная активность на устройстве приложению, сравнивая имена пакетов:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

этот метод был найден в структуре Droid-Fu (теперь называемой зажиганием).

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

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

когда число выполняемых действий достигает 0, приложение помещается в фоновый режим, если выполняются следующие условия:

  • приостановленное действие не завершено (была использована кнопка" Назад"). Это можно сделать с помощью метода activity.isFinishing()
  • новое действие (то же имя пакета) не запускается. Вы можете переопределить startactivity () метод, чтобы установить переменную, которая указывает на это, а затем сбросить его в onPostResume (), который является последним методом для запуска при создании/возобновлении действия.

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


создать класс что расширяет Application. Тогда в нем мы можем использовать его метод override,onTrimMemory().

чтобы определить, если приложение пошло в фоновом режиме, мы будем использовать:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }

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

когда вызывается UserLeaveHint, вы можете установить логический флаг inBackground в true. Когда onResume вызывается, только предположим, что вы вернулись на передний план, если установлен флаг inBackground. Это потому, что onResume также будет вызываться на вашей основной деятельности, если пользователь был только в меню настроек и никогда не покидал приложение.

помните, что если пользователь нажимает кнопку "Домой" на экране настроек, onUserLeaveHint будет вызван в вашей активности настроек, и когда они вернутся onResume будет называться в вашей активности. Если у вас есть только этот код обнаружения в вашей основной деятельности, вы пропустите этот вариант использования. Чтобы иметь этот код во всех ваших действиях без дублирования кода, имейте абстрактный класс activity, который расширяет Activity, и поместите в него свой общий код. Тогда каждое действие, которое у вас есть, может расширить это абстрактное действие.

например:

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}

ActivityLifecycleCallbacks может представлять интерес, но это не хорошо документировано.

хотя, если вы называете registerActivityLifecycleCallbacks () вы должны иметь возможность получать обратные вызовы для создания, уничтожения и т. д. Вы можете позвонить getComponentName() для деятельности.


в вашем приложении добавьте обратный вызов и проверьте корневую активность следующим образом:

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

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

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}

Я создал проект на Github app-передний план-фон-слушайте

создайте BaseActivity для всех действий в приложении.

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

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

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

теперь используйте эту BaseActivity как супер класс всей Вашей деятельности, как MainActivity расширяет BaseActivity и onAppStart будет вызываться при запуске приложения и onAppPause () будет вызываться, когда приложение идет фон с любого экрана.


Это довольно легко с ProcessLifecycleOwner

добавить зависимости

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

на Котлин:

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

затем в вашей базовой деятельности:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

смотрите мою статью на эту тему: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48


Я нашел хороший метод для обнаружения приложения, будь то ввод переднего плана или фона. Вот мой код. Надеюсь, это поможет вам.

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}


Edit 2: то, что я написал ниже, на самом деле не будет работать. Google отклонил приложение, которое включает вызов ActivityManager.getRunningTasks (). От документация очевидно, что этот API предназначен только для отладки и целей развития. Я буду обновлять этот пост, как только у меня будет время обновить проект GitHub ниже с новой схемой, которая использует таймеры и почти так же хороша.

Edit 1: я написал блоге и создал простой репозиторий GitHub сделать это очень легко.

принятый и топ-рейтинг ответ оба не очень лучший подход. Реализация isapplicationbroughttobackground() с наивысшим рейтингом ответа не обрабатывает ситуацию, когда основное действие приложения уступает действию, которое определено в том же приложении, но имеет другой пакет Java. Я придумал способ сделать это, что будет работать в этом случае.

звонок onPause (), и он сообщит вам, идет ли ваше приложение в фоновом режиме, потому что другое приложение запустилось, или пользователь нажал кнопку "Домой".

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

вы можете использовать:

защищенный void onRestart ()

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

enter image description here


мое решение было вдохновлено ответом @d60402, а также полагается на временное окно, но не использует Timer:

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

здесь SingletonApplication расширение Application класс:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}

я использовал это с помощью Google Analytics EasyTracker, и это сработало. Он может быть расширен, чтобы сделать то, что вы ищете, используя простое целое число.

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        appBackgroundedDetector();
    }

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

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

создать жизнедеятельность обратного цикла, как это:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

и просто зарегистрируйте его в своем классе приложений, как показано ниже:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}

это модифицированная версия ответа @d60402:https://stackoverflow.com/a/15573121/4747587

сделайте все, что там упомянуто. Но вместо того, чтобы Base Activity и делает это в качестве родителя для каждого действия и переопределения onResume() и onPause, сделайте следующее:

в своем классе приложений добавьте строку:

registerActivityLifecycleCallbacks(приложение.ActivityLifecycleCallbacks callback);

этот callback все методы жизненного цикла активности, и теперь вы можете переопределить onActivityResumed() и onActivityPaused().

взгляните на эту суть:https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b


то, что я сделал, это убедиться, что все действия в приложении запускаются с startActivityForResult затем проверка, вызывался ли onActivityResult перед onResume. Если это не так, это означает, что мы только что вернулись откуда-то вне нашего приложения.

boolean onActivityResultCalledBeforeOnResume;

@Override
public void startActivity(Intent intent) {
    startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    onActivityResultCalledBeforeOnResume = true;
}

@Override
protected void onResume() {
    super.onResume();
    if (!onActivityResultCalledBeforeOnResume) {
        // here, app was brought to foreground
    }
    onActivityResultCalledBeforeOnResume = false;
}

Это мое решение https://github.com/doridori/AndroidUtils/blob/master/App/src/main/java/com/doridori/lib/app/ActivityCounter.java

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


вот мое решение. Просто зарегистрируйте этот ActivityLifecycleCallbacks в своем основном классе приложения. В комментариях я упоминаю случай edge активности профиля пользователя. Это просто деятельность с прозрачными краями.

/**
 * This class used Activity lifecycle callbacks to determine when the application goes to the
 * background as well as when it is brought to the foreground.
 */
public class Foreground implements Application.ActivityLifecycleCallbacks
{
    /**
     * How long to wait before checking onStart()/onStop() count to determine if the app has been
     * backgrounded.
     */
    public static final long BACKGROUND_CHECK_DELAY_MS = 500;

    private static Foreground sInstance;

    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
    private boolean mIsForeground = false;
    private int mCount;

    public static void init(final Application application)
    {
        if (sInstance == null)
        {
            sInstance = new Foreground();
            application.registerActivityLifecycleCallbacks(sInstance);
        }
    }

    public static Foreground getInstance()
    {
        return sInstance;
    }

    public boolean isForeground()
    {
        return mIsForeground;
    }

    public boolean isBackground()
    {
        return !mIsForeground;
    }

    @Override
    public void onActivityStarted(final Activity activity)
    {
        mCount++;

        // Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
        // the app before it runs.
        mMainThreadHandler.removeCallbacksAndMessages(null);

        if (!mIsForeground)
        {
            mIsForeground = true;
        }
    }

    @Override
    public void onActivityStopped(final Activity activity)
    {
        mCount--;

        // A transparent Activity like community user profile won't stop the Activity that launched
        // it. If you launch another Activity from the user profile or hit the Android home button,
        // there are two onStops(). One for the user profile and one for its parent. Remove any
        // posted Runnables so we don't get two session ended events.
        mMainThreadHandler.removeCallbacksAndMessages(null);
        mMainThreadHandler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                if (mCount == 0)
                {
                    mIsForeground = false;
                }
            }
        }, BACKGROUND_CHECK_DELAY_MS);
    }

    @Override
    public void onActivityCreated(final Activity activity, final Bundle savedInstanceState)
    {

    }

    @Override
    public void onActivityResumed(final Activity activity)
    {

    }

    @Override
    public void onActivityPaused(final Activity activity)
    {

    }

    @Override
    public void onActivitySaveInstanceState(final Activity activity, final Bundle outState)
    {

    }

    @Override
    public void onActivityDestroyed(final Activity activity)
    {

    }
}

мое приложение должно "перезагрузиться" после возвращения из фона - показать ряд мероприятий, в соответствии с просьбами клиентов. После обширного поиска о том, как управлять переходами фона/переднего плана (трактованными очень по-разному между iOS и Android), я пересек этот вопрос. Нашел здесь очень полезную помощь, особенно от самого проголосовавшего ответа и отмеченного как правильный. Однако просто переустановите корневую активность каждый раз, когда приложение выходит на передний план, выглядело слишком раздражающим, когда вы подумайте об UX. Решение, которое сработало для меня, и я думаю, что наиболее адекватным-на основе функциональности приложений Youtube и Twitter-было объединить ответы от @GirishNair и @d60402: вызов таймера при обрезке памяти приложения следующим образом:

@Override
public void onTrimMemory(int level) {
    if (stateOfLifeCycle.equals("Stop")) {
        startActivityTransitionTimer();
    }

    super.onTrimMemory(level);
}

мой лимит таймера установлен на 30 секунд - я думаю об увеличении этого немного.

private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;

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

на расширение приложения:

@Override
public void onActivityCreated(Activity activity, Bundle arg1) {
    stopActivityTransitionTimer();
    stateOfLifeCycle = "Create";
}

@Override
public void onActivityDestroyed(Activity activity) {
    stopActivityTransitionTimer();
    stateOfLifeCycle = "Destroy";
}

о деятельности (предпочтительно о базовой деятельности, унаследованной другими):

@Override
protected void onStart() {
    super.onStart();
    if (App.wasInBackground) {
        stopActivityTransitionTimer();
    }
}

в моем случае, когда приложение выходит на передний план после максимального времени, создается новая задача, поэтому stopActivityTransitionTimer() вызывается onActivityCreated () или onActivityDestroyed (), в классе расширения приложения - становится ненужным вызывать метод в действии. Надеюсь, это поможет.


Как насчет такого решения

public class BaseActivity extends Activity
{

    static String currentAct = "";

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

        if (currentAct.equals(""))
            Toast.makeText(this, "Start", Toast.LENGTH_LONG).show();

        currentAct = getLocalClassName();
    }

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

        if (currentAct.equals(getLocalClassName()))
        {
            currentAct = "";
            Toast.makeText(this, "Stop", Toast.LENGTH_LONG).show();
        }
    }
}

все действия должны расширять BaseActivity.

когда действие вызывает другое (A - >B), currentAct не равно getLocalClassName (), потому что onStart () второго действия (B) вызывается перед onStop () первого (A) (https://developer.android.com/guide/components/activities.html#CoordinatingActivities).

когда потребитель отжимает домашнюю кнопку или изменяет между применением будет просто вызовите onStop (), а затем currentAct равен getLocalClassName ().


это, по-видимому, один из самых сложных вопросов в Android, так как (на момент написания этой статьи) Android не имеет эквивалентов iOS applicationDidEnterBackground() или applicationWillEnterForeground() обратные вызовы. Я использовал Библиотека AppState это было собрано @jenzz.

[AppState] простая, реактивная библиотека Android на основе RxJava, которая отслеживает изменения состояния приложения. Он уведомляет подписчиков каждый раз, когда приложение переходит в фоновый режим и возвращается в передний план.

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

сначала я добавил Эти зависимости в gradle:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

тогда было просто добавить эти строки в соответствующее место в вашем коде:

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

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


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

для более подробной информации об этом работает, сильный текст нажмите здесь

import android.content.ComponentCallbacks2;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

private Context context;
private Toast toast;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    context = this;
}

private void showToast(String message) {
    //If toast is already showing cancel it
    if (toast != null) {
        toast.cancel();
    }

    toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
    toast.show();
}

@Override
protected void onStart() {
    super.onStart();
    showToast("App In Foreground");
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        showToast("App In Background");
    }
  }
}

создать класс с именем MyApp, как показано ниже:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

затем, везде, где вы хотите (лучше первое действие, запущенное в приложении), добавьте код ниже:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

готово! Теперь, когда приложение находится в фоновом режиме, мы получаем журнала status : we are out и когда мы заходим в приложение, мы получаем журнала status : we are out


вы можете легко достичь этого с помощью ActivityLifecycleCallbacks и ComponentCallbacks2 что-то вроде ниже.

создать класс AppLifeCycleHandler реализация вышеуказанных интерфейсов.

package com.sample.app;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

  @Override
  public void onActivityCreated(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

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

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

надеюсь, что это помогает.

редактировать В качестве альтернативы теперь вы можете использовать Life cycle aware компонент архитектуры.