CyberAgent Developers Advent Calendar 2017の5日目です。
今日はAbemaTVで最近導入したProcessLifecycleOwner
について紹介したいと思います。
ProcessLifecycleOwner
今まで、Androidではアプリケーションのバックグラウンド⇆フォアグラウンド検知をすることが難しいとされていました。Activity自体のバックグラウンド⇆フォアグラウンドはonStart/onResumeやonPause/onStopがあるため可能でしたが、Applicationクラスには、onCreate/onTerminateしかありません。
そこで登場したのが ProcessLifecycleOwner
です。
公式サイトはこちら
このProcessLifecycleOwner
を使うことでApplicationプロセスの状態を検知することができます。
導入方法
まずはgradleからLifecycleComponentを取り込みます。
implementation "android.arch.lifecycle:extensions:1.0.0"
kapt "android.arch.lifecycle:compiler:1.0.0"
Applicationクラス
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
公式サイトにあるようにLifecycle.Event.ON_DESTROY
は呼ばれません。
onTerminate()も呼ばれません。
onTerminate()やLifecycle.Event.ON_DESTROY
が呼ばれるタイミングではプロセスが破棄されているからかもしれません。
実際に試して見ても、onTerminate()とLifecycle.Event.ON_DESTROY
は呼ばれていませんでした。
/**
* 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
の中身を見てみます。
以下では必要な部分だけ抜粋して記載しています。
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()が呼ばれると、アプリがバックグラウンド->フォアグラウンドへ移行したと認識されます。
最後に
簡単なサンプルアプリを作りましたので、ぜひ見て見てください。