今日は 1,040 円だった。
昼に 380 円のモスバーガー単品、夜にうどんだった。
アプリを起動していない状態で常に現在位置をサーバーに送信するためには、けっこう長い手順を踏まなければならない。事前知識のない状態で実装することは僕にはできなかった。ある程度知識を身に付けた状態でようやく実装し終えることができた。
まずサービスについて。
サービスはアプリが非アクティブ状態になっても常に動作し続ける。しかし非アクティブ状態のサービスは制限を受ける。たとえば位置情報サービスの場合、1時間に数回しか現在位置を取得できなくなる。アクティブ状態であればこのような制限は受けない。
サービスであってもこのような制限を受けたくない場合がある。
そういったときのために、フォアグラウンドサービスが用意されている。
フォアグラウンドサービスであれば制限を受けないが、その代わりに通知領域にサービスの内容を表示し続けなければならない。この通知領域の作り方も一工夫要る。
これはドキュメントに載っていることをそのまま実装すればいいが、何もわからない状態だと、たとえ写経するだけであったとしても辛かったりする。
具体的には、次のようなルールを満たすコードを書く必要がある。
onStartCommand
コールバックメソッド内で、startForeground()
を使って通知領域を作成する- サービスの開始は
startForegroundService()
で行う ACCESS_BACKGROUND_LOCATION
を有効にしなければならないACCESS_COARSE_LOCATION
権限の要求とACCESS_BACKGROUND_LOCATION
権限の要求は、完全に別々に行わなければならない
これらはコンパイル時にエラーが確認できず、すべて実行時に確認できる。
これがけっこう辛い。
最後については、requestPermissions(arrayOf(ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION), 123)
を呼び出してもエラーが出ない。
色々と頑張る必要がある。
通知の作成にも一癖あって、ただ Notification
オブジェクトを作って startForeground()
に渡せばいいというものではなく、その前に createNotificationChannel
で通知チャンネルを作成しなければならない。
以上のことをまとめると onStartCommand
コールバックメソッドの実装は次のようになる。
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val id = "id" if (manager.getNotificationChannel(id) == null) { val mChannel = NotificationChannel(id, "name", NotificationManager.IMPORTANCE_HIGH) mChannel.apply { description = "description" } manager.createNotificationChannel(mChannel) } val notification = NotificationCompat.Builder(this, id).apply { setContentTitle("content title") setContentText("content text") setSmallIcon(R.drawable.ic_launcher_background) }.build() startForeground(1, notification) return START_STICKY }
AndroidManifest.xml と MainActivity.kt(のonCreate
)には次のように書く。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val btnA = findViewById<Button>(R.id.buttonA) btnA.setOnClickListener { requestPermissions(arrayOf( ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, ), 123) } val btnB = findViewById<Button>(R.id.buttonB) btnB.setOnClickListener { requestPermissions(arrayOf( ACCESS_BACKGROUND_LOCATION, ), 123) } val btnC = findViewById<Button>(R.id.buttonC) btnC.setOnClickListener { val intent = Intent(this, MyService::class.java) startForegroundService(intent) } }