背景

自作の Service が起動中かどうかをチェックしたかったので調べたのでメモ.

検索してみると, 以下の StackOverflow に最終的に行き着いた.

How to check if a service is running on Android? - stackoverflow

しかし, 上記の方法だと, ActivityManager#getRunningService(Int) が Android 8.0 から Deprecated になっているため, 現在は使うことができない.

他の手段を調べた結果, LocalBroadcastManagerを使えば出来そうだということが分かったので, これを使って実装してみた.

LocalBroadcastManager

自身のプロセス内にのみBroadcastされる Intent を発行できるやつです. 受信側が自身のプロセス内であることが保証されるので安全です. (Deeplink を自身のプロセスで起動した場合はその限りではないかも)

EventBus なんかと似たような動作をします.

実装

LocalBroadcastManager#sendBroadcast は送信に成功したかどうか(受信側が存在していたるかどうか)を返り値で取得できる. つまり, Service が起動している間だけ受信側を用意すれば, その返り値によって Service の起動状態を確認することができる.

具体的には, Service 側で以下のような実装をする.

class HelloService : Service() {

    companion object {
        const val ACTION_IS_RUNNING = "com.example.HelloService_is_running"

        fun createIntent(context: Context) = Intent(context, HelloService::class.java)

        fun isRunning(context: Context): Boolean {
            return LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(ACTION_IS_RUNNING))
        }
    }

    private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(applicationContext) }

    // BroadcastReceiver側の処理は不要
    private val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            // no op
        }
    }

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onCreate() {
        super.onCreate()
        // Receiver の登録
        localBroadcastManager.registerReceiver(broadcastReceiver, IntentFilter(ACTION_IS_RUNNING))
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // startForegroundService(..., ...)
        return START_STICKY
    }

    override fun onDestroy() {
        // Receiver の登録解除
        localBroadcastManager.unregisterReceiver(broadcastReceiver)
        super.onDestroy()
    }

onDestroy で登録解除をしているが, 実際には無くても問題はない. Service が stop されたときやシステムによってキルされたときにも, 同様に BroadcastReciver も無効になるからである.

Service側の comapnion object に isRunning(context: Context) を用意し, このメソッドを呼ぶだけで起動状態を確認できるようにした. これにより, Activity などの確認側に LocalBroadcastManager などの知識を持ち込まずに済む.

以上の実装によって, 確認側は HelloService.isRunning(context) を呼ぶだけで Service の起動状態を確認できるようになった.

まとめ

  • 調べて出てくるのは Deprecated になった方法だけ 😲

  • LocalBroadcastManager を用いて Servie の起動状態をチェックできた