Как определить, когда приложение Android переходит в фоновый режим и возвращается на передний план - PullRequest
353 голосов
/ 11 декабря 2010

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

Ответы [ 38 ]

1 голос
/ 22 сентября 2017

Похоже, это один из самых сложных вопросов в Android, поскольку (на момент написания статьи) Android не имеет эквивалентов iOS для обратных вызовов applicationDidEnterBackground() или applicationWillEnterForeground().Я использовал AppState Library , который был составлен с помощью @ 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;
        }
    }
});

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

1 голос
/ 08 мая 2015

Мое решение было вдохновлено ответом @ 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;
}
1 голос
/ 05 июля 2018

Поскольку я не нашел никакого подхода, который также обрабатывает вращение без проверки меток времени, я подумал, что я также поделюсь тем, как мы сейчас делаем это в нашем приложении. Единственное дополнение к этому ответу https://stackoverflow.com/a/42679191/5119746 состоит в том, что мы также принимаем во внимание ориентацию.

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

Затем, для обратных вызовов у нас сначала есть резюме:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

и onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

А потом, вот дополнение: Проверка на изменение ориентации:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

Вот и все. Надеюсь, это кому-нибудь поможет:)

1 голос
/ 24 февраля 2015

Я использовал это с 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) {

            }
        });
    }
}
1 голос
/ 17 апреля 2018

Вы можете легко добиться этого с помощью 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();
  }
}

InВаш класс, который расширяет 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");
    }
}

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

РЕДАКТИРОВАТЬ В качестве альтернативы теперь вы можете использовать компонент архитектуры с учетом жизненного цикла.

1 голос
/ 25 сентября 2017

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

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

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

registerActivityLifecycleCallbacks (обратный вызов Application.ActivityLifecycleCallbacks);

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

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

1 голос
/ 15 мая 2017

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

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

 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());
}
0 голосов
/ 24 апреля 2019

Мы можем расширить это решение , используя LiveData:

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

Теперь мы можем подписаться на эту LiveData и перехватить необходимые события.Например:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}
0 голосов
/ 21 ноября 2014

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

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

0 голосов
/ 28 ноября 2018

Мне удалось отследить переход приложений в фоновый режим и обратно на передний план, реализовав BaseActivity, в котором используются обратные вызовы действий onResume, onPause и onStop.Вот мои реализации.

override fun onResume() {
    super.onResume()
    if (AppActivityState.state == AppState.ON_LAUNCHED) {
        // We are in the first launch.
        onLaunched()
    } else {
        if (AppActivityState.state == AppState.ON_BACKGROUND) {
            // We came from background to foreground.
            AppActivityState.state = AppState.ON_FOREGROUND
            onForeground()
        } else {
            // We are just navigating through pages.
            AppActivityState.state = AppState.RESUMED
        }
    }
}

override fun onPause() {
    super.onPause()
    // If state is followed by onStop then it means we will going to background.
    AppActivityState.state = AppState.PAUSED
}

override fun onStop() {
    super.onStop()

    // App will go to background base on the 'pause' cue.
    if (AppActivityState.state == AppState.PAUSED) {
        AppActivityState.state = AppState.ON_BACKGROUND
        onBackground()
    }
}

После создания BaseActivity вам просто нужно расширить это действие на любое действие в вашем приложении.

В этом типе реализации вы можете точно определить следующее: - onBackground> приложение перейдет на задний план - onForeground> приложение вернется на передний план - onLaunch> приложение только что открылось

Надеюсь, это поможет вам:)

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