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

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

Ответы [ 38 ]

13 голосов
/ 07 ноября 2011

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

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

10 голосов
/ 23 ноября 2018

Пакет android.arch.lifecycle предоставляет классы и интерфейсы, позволяющие создавать компоненты, поддерживающие жизненный цикл

Ваше приложение должно реализовывать интерфейс LifecycleObserver:

public class MyApplication extends Application implements LifecycleObserver {

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

Для этого вам нужно добавить эту зависимость в файл build.gradle:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

В соответствии с рекомендациями Google вы должны минимизировать код, выполняемый в методах действий жизненного цикла:

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

Подробнее можно прочитать здесь: https://developer.android.com/topic/libraries/architecture/lifecycle

8 голосов
/ 11 сентября 2013

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

@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();
            }
        }
    });
}
6 голосов
/ 06 августа 2015

Я создал проект на Github app-foreground-background-listen

Создайте 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 () - при переходе приложения в фоновый режим с любого экрана.

6 голосов
/ 03 января 2018

Это довольно просто с 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

3 голосов
/ 03 августа 2012

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

Редактировать 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;
}
3 голосов
/ 12 ноября 2015

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

/**
 * 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();

}

3 голосов
/ 09 марта 2016

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

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

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

enter image description here

3 голосов
/ 21 января 2019

Не существует простых методов жизненного цикла, которые бы сообщали вам, когда все приложение переходит в фоновый режим / передний план.

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

С небольшим обходным путем, это возможно. Здесь ActivityLifecycleCallbacks приходит на помощь. Позвольте мне пройти шаг за шагом.

  1. Сначала создайте класс, который расширяет android.app.Application и реализует интерфейс ActivityLifecycleCallbacks . В Application.onCreate () зарегистрируйте обратный вызов.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
    
  2. Зарегистрируйте класс «App» в манифесте, как показано ниже, <application android:name=".App".

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

    Объявите 2 переменные, как показано ниже в классе «App».

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;
    

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

  4. Используя следующий код, вы можете определить, выходит ли приложение на передний план.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
    
  5. Это как определить, работает ли приложение в фоновом режиме.

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }
    

Как это работает:

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

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

A.onCreate ()

A.onStart () (++ activityReferences == 1) (приложение выходит на передний план)

A.onResume ()

Теперь упражнение A начинает упражнение B.

A.onPause ()

B.onCreate ()

B.onStart () (++ ActivityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

Затем пользователь возвращается из действия B,

B.onPause ()

A.onStart () (++ ActivityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

Затем пользователь нажимает кнопку «Домой»,

A.onPause ()

A.onStop () (--activityReferences == 0) (приложение входит в фоновый режим)

В случае, если пользователь нажимает кнопку «Домой» в «Деятельности B» вместо кнопки «Назад», он все равно будет таким же, а ActivityReferences будет 0. Следовательно, мы можем определить, как приложение входит в фоновый режим.

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

B.onPause ()

B.onStop () (--activityReferences == 0) (приложение входит в фон ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (приложение выходит на передний план ??)

B.onResume ()

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

2 голосов
/ 15 марта 2018

Правильный ответ здесь

Создать класс с именем 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

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