Infinito Nirone 7

白羽の矢を刺すスタイル

Android O からの Service を foreground で動かすときのベストプラクティス

Android Oreo から、Oreo 以上のバージョンをtargetSdkVersionにしているアプリケーションがForeground Serviceを起動するには、Context#startForegroundService()によるサービスの起動とService#startForeground()による通知の表示の両方を実行しなければならなくなりました。またContext#startForegroundService()から5秒以内にService#startForeground()を呼び出さないと、ANRとしてクラッシュレポートが作成されます。

この"Context#startForegroundService()から5秒以内にService#startForeground()を呼ぶ"というのが厄介で、ServiceがContext#startForegroundService()で動き始めたら問答無用で5秒のカウントが始まるため、その5秒以内にService#stopSelf()などでサービスを止めてもANRになります。 さらに、ANRのときに吐き出されるスタックトレース(実際には例外が飛んでアプリがクラッシュするのでクラッシュレポートも作られるはず)には、Service#startForeground()を呼ばなかったこと以外の情報が皆無なので、ログ出力などツールの力を借りる必要があります。

ここでServiceの起動にいくつかのパラメータが必要なパターンを考えてみます。ServiceActivityと同様に自分で直接インスタンスをつくることはしないので、Intentにパラメータを突っ込んで起動させます。このとき、パラメータの検証をServiceの責務として、検証が通らないとき(≒Serviceが期待するパラメータでないとき)にService#stopSelf()で止めてしまうと、先程説明したとおりService#startForeground()を呼ばなかったとしてクラッシュしてしまいます。

よって、Android O からはContext#startForegroundService()Serviceを起動させる側がServiceにわたすパラメータの検証をしなければなりません。