[Android] アプリのbackground/foregroundを検知する

このエントリーをはてなブックマークに追加

単純にonResume/onStartでバックグラウンド復帰時の処理を書くとActivityの生成時やバックキーで戻ってきた時等、処理しなくていいタイミングでもコードが走ってしまいます。

ActivityManager#getRunningAppProcessesで実行中のプロセスを取得し、アプリのforeground/backgroundステータスを見ることもできますが、一部端末でうまく動作しないことがあるようです(そもそもbackground/foregroundを判定するためだけに実行中のプロセスを全部調べるのもアホくさい気がします)。

そこで、API14(ICS)から追加された、Application.ActivityLifecycleCallbacksを利用します。このAPIを利用すると、すべてのActivityのライフサイクルを監視し、任意の処理を実行することができます。

コードは下記のとおりです。

public class MyApp extends Application {

    private AppStatus mAppStatus = AppStatus.FOREGROUND;

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

        registerActivityLifecycleCallbacks(new MyActivityLifecycleCallbacks());
    }

    public MyApp get(Context context) {
        return (MyApp) context.getApplicationContext();
    }

    public AppStatus getAppStatus() {
        return mAppStatus;
    }

    // check if app is foreground
    public boolean isForeground() {
        return mAppStatus.ordinal() > AppStatus.BACKGROUND.ordinal();
    }

    public enum AppStatus {
        BACKGROUND,                // app is background
        RETURNED_TO_FOREGROUND,    // app returned to foreground(or first launch)
        FOREGROUND;                // app is foreground
    }

    public class MyActivityLifecycleCallbacks implements ActivityLifecycleCallbacks {

        // running activity count
        private int running = 0;

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

        }

        @Override
        public void onActivityStarted(Activity activity) {
            if (++running == 1) {
                // running activity is 1,
                // app must be returned from background just now (or first launch)
                mAppStatus = AppStatus.RETURNED_TO_FOREGROUND;
            } else if (running > 1) {
                // 2 or more running activities,
                // should be foreground already.
                mAppStatus = AppStatus.FOREGROUND;
            }
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityStopped(Activity activity) {
            if (--running == 0) {
                // no active activity
                // app goes to background
                mAppStatus = AppStatus.BACKGROUND;
            }
        }

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

        @Override
        public void onActivityDestroyed(Activity activity) {
        }
    }
}

あとは任意の場所でMyApp.get(getContext()).getAppStatus()とか、MyApp.get(getContext()).isForeground()とか呼んであげれば、アプリが現在foregroundにいるのかbackgroundにいるのか、判定することができます。

backgroundからforegroundに復帰した時かどうか知りたい!ってときはMyApp.get(getContext()).getAppStatus()AppStatus.RETURNED_TO_BACKGROUNDと比較してあげればいいです。

軽く仕組みを説明すると、ActivityonStart/onStopに相当するonActivityStarted/onActivityStoppedで現在アクティブなActivityをカウントしているだけです。

Activity間を遷移していると、最低でも今いるActivityと、一つ前のActivityがアクティブな状態になります(running > 1な状態)。
アプリがbackgroundになると、すべてのActivityonStopを通るので、非アクティブな状態になります(running == 0な状態)。
また、アプリがforegroundに復帰すると直前まで表示されていたActivityonStartのみが実行されるので、running == 1になります。

この状態の変化を利用して、アプリのbackground/foregroundステータスを検知するのが上記のコードです。

Activityが複数作られる、という前提のコードなので1Activity複数FragmentなアプリやMortar/Flowなアプリだと意味がなさそうです。

こちらからは以上です。