[Android]バックグラウンドでセンサーなどのログを取得し続けるには

バックグラウンドからサービスを起動できなくなった

ライフログなどのログを取得し続けるアプリを作ろうと思うと、アプリを閉じた状態でもセンサーなどの値を監視し続ける必要がある。

そのようなログアプリで端末再起動後も自動でログの取得を開始するようにしていると、下記のようなエラーでサービスが起動できなくなった。

Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.muaaru.testbackgroundservice/.ForegroundService }: app is in background uid UidRecord{c4b2502 u0a100 RCVR idle change:uncached procs:1 seq(0,0,0)}

どうもAndroid O (8) 以降では下記の制限に引っかかってしまうようになったようだ。

バックグラウンド実行制限

バックグラウンドからサービスを起動するには

回避するには、サービスをフォアグランドサービスとして起動する必要がある。

公式ドキュメントの説明だと、NotificationManager.startServiceInForegroundを使用すると書かれているが、
そのメソッドが見当たらず、実際はContextCompat.startForegroundServiceを使用するらしい。

ContextCompat.startForegroundServiceの実装を見てみると、Androidのバージョンによって切り替えているだけだった。

    public static void startForegroundService(@NonNull Context context, @NonNull Intent intent) {
        if (VERSION.SDK_INT >= 26) {
            context.startForegroundService(intent);
        } else {
            context.startService(intent);
        }
    }

minSdkVersionを26以降にできるなら、ContextのstartForegroundServiceで良さそうである。

さらに、Android P以降だと、startForegroundServiceする際に、パーミッションが必要になり、

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

AndroidManifestに上記の記述が必要になっている。

起動したサービスをフォアグランドサービスに設定する

バックグラウンドからstartForegroundServiceでサービスを起動するが、これだけではフォアグランドサービスには、まだならない。

サービス側で、onStartCommandがよびだされるので、さらにServiceのstartForegroundメソッドを呼び出して、サービスをフォアグランドサービスとして設定する。
startForegroundする際に、Notificationが必要になってくる。

startForeground(ForegroundServiceNotification.FOREGROUND_SERVICE_NOTIFICATION_ID, notification);

フォアグラウンドサービスに設定する通知について

Android O 以降、すべての通知をチャンネルに割り当てる必要がある。

下記のようにしてチャンネルを設定するのだが、フォアグラウンドサービスに設定するNotificationChannel特有の注意点があった。

NotificationChannel notificationChannel = new NotificationChannel(
                CHANNEL_ID,
                CHANNEL_NAME,
                NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(notificationChannel);

フォアグラウンドサービスの通知の場合、公式ドキュメントにはIMPORTANCE_MIN は非推奨と記述されている。
https://developer.android.com/reference/android/app/NotificationManager.html#IMPORTANCE_MIN

ただ、実際に動作を見てみると、IMPORTANCE_HIGH 未満を指定しても、IMPORTANCE_HIGHとして扱われていた。

このあたりは、ユーザに気づかれずにバックグラウンドで処理ができなくなるようにするため、と考えると当然かもしれない。

まとめ

今回の調査で作成したソースはGitHubで公開してます。
https://github.com/muaaru/testbackgroundservice

バックグラウンドでサービスを起動するのに把握しなければいけないことが、だいぶ増えている。

特に通知が必須になっているので、対応するには新規に通知の処理を作らないといけなくなっている。
また、公式ドキュメントに記載がない仕様がいろいろとあり、動作を調べながらでないと予期しない動きとなりそうである。