アプリのバックグラウンド⇆フォアグラウンドを検知する。ProcessLifecycleOwnerの導入

CyberAgent Developers Advent Calendar 2017の5日目です。
今日はAbemaTVで最近導入した`ProcessLifecycleOwner`について紹介したいと思います。

# ProcessLifecycleOwner
今まで、Androidではアプリケーションのバックグラウンド⇆フォアグラウンド検知をすることが難しいとされていました。Activity自体のバックグラウンド⇆フォアグラウンドはonStart/onResumeやonPause/onStopがあるため可能でしたが、Applicationクラスには、onCreate/onTerminateしかありません。

そこで登場したのが `ProcessLifecycleOwner` です。

公式サイトは[こちら](https://developer.android.com/reference/android/arch/lifecycle/ProcessLifecycleOwner.html)

この`ProcessLifecycleOwner`を使うことでApplicationプロセスの状態を検知することができます。


# 導入方法

まずはgradleからLifecycleComponentを取り込みます。

```build.gradle
implementation "android.arch.lifecycle:extensions:1.0.0"
kapt "android.arch.lifecycle:compiler:1.0.0"
```

Applicationクラス

```app.kt
class App : Application(), LifecycleObserver {

  override fun onCreate() {
    super.onCreate()
    ProcessLifecycleOwner.get().lifecycle.addObserver(this)
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
  fun onAppCreate() {
    Log.i("process", "onCreate")
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_START)
  fun onAppStart() {
    Log.i("process", "onStart")
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
  fun onAppResume() {
    Log.i("process", "onStart")
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
  fun onAppPause() {
    Log.i("process", "onPause")
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
  fun onAppStop() {
    Log.i("process", "onStop")
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
  fun onAppDestroy() {
    Log.i("process", "onDestroy")
  }

}
```

これでアプリケーションプロセスの状態を検知することができます。


# バックグラウンド⇆フォアグラウンド検知

バックグラウンド->フォアグラウンド、フォアグラウンド->バックグラウンドのタイミングで何かしらの処理をしたい場合、 `Lifecycle.Event.ON_START` と `Lifecycle.Event.ON_STOP` で処理を行った方がいいです。

理由は、マルチウインドウやPictureInPicture使用時に `Lifecycle.Event.ON_RESUME` や `Lifecycle.Event.ON_PAUSE` が呼ばれてしまうからです。

# Lifecycle.Event.ON_DESTROY / onTerminate()
>  ON_DESTROY will never be dispatched

[公式サイト](https://developer.android.com/reference/android/arch/lifecycle/ProcessLifecycleOwner.html)にあるように`Lifecycle.Event.ON_DESTROY` は呼ばれません。

onTerminate()も呼ばれません。
onTerminate()や`Lifecycle.Event.ON_DESTROY`が呼ばれるタイミングではプロセスが破棄されているからかもしれません。
実際に試して見ても、onTerminate()と`Lifecycle.Event.ON_DESTROY`は呼ばれていませんでした。

```Application.java
    /**
     * This method is for use in emulated process environments.  It will
     * never be called on a production Android device, where processes are
     * removed by simply killing them; no user code (including this callback)
     * is executed when doing so.
     */
    @CallSuper
    public void onTerminate() {
    }
```


# どのように実現しているのか

Processのバックグラウンド⇆フォアグラウンドはどのように判定しているのでしょうか？

実際に`ProcessLifecycleOwner`の中身を見てみます。
以下では必要な部分だけ抜粋して記載しています。

```ProcessLifecycleOwner.java

public class ProcessLifecycleOwner implements LifecycleOwner {

　　　　static final long TIMEOUT_MS = 700; //mls
  private int mResumedCounter = 0;

　　　　・・・

　　　　private Runnable mDelayedPauseRunnable = new Runnable() {
    　　　　@Override
    　　　　public void run() {
    　　　　    dispatchPauseIfNeeded();
    　　　　    dispatchStopIfNeeded();
   　　　　 }
　　　　};

　　　　・・・

  void activityResumed() {
    mResumedCounter++;
    if (mResumedCounter == 1) {
        if (mPauseSent) {
            mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
            mPauseSent = false;
        } else {
            mHandler.removeCallbacks(mDelayedPauseRunnable);
        }
    }
  }

　　　　void activityPaused() {
   　　　　 mResumedCounter--;
    　　　　if (mResumedCounter == 0) {
     　　　　   mHandler.postDelayed(mDelayedPauseRunnable, TIMEOUT_MS);
   　　　　 }
　　　　}

　　　　・・・

}

```

`ProcessLifecycleOwner`は180行の比較的小さなクラスです。
中で何をしているかというとまず、`LifecycleOwner`をimplementsしてActivityのLifecycleを観測しています。
あるActivityがonPause()に入った段階で、mDelayedPauseRunnableを0.7秒後に呼び出しています。
0.7秒間の間にどこかのActivityでonResume()が呼び出されると、mDelayedPauseRunnableがremoveされますが、逆に0.7秒間でActivity#onResume()が呼ばれなければ、ProcessLifecycleOwnerはApplicationはonPauseとして検知されます。

**つまり、Activity#onPause()が呼ばれて、0.7秒間Activity#onResume()が呼び出されなければ、アプリがフォアグラウンド->バックグラウンドへ移行したと認識されます。**

**また、Applicationの`Lifecycle.Event.ON_PAUSE`が呼ばれた状態で、Activity#onResume()が呼ばれると、アプリがバックグラウンド->フォアグラウンドへ移行したと認識されます。**

# 最後に
簡単なサンプルアプリを作りましたので、ぜひ見て見てください。


https://github.com/TakuSemba/ProcessLifecycleOwnerSample/tree/master