はじめに
Bluetooth(以降BLE)周辺機器と連携するアプリで、「デバイスが近くに来たら自動でデータ同期したい」となったので、フォアグラウンドサービスを常時起動するのはバッテリー消費の観点から避けたく、Android 13 で追加された CompanionDeviceManager(以降CDM)の presence observation を採用したので今回はその時のメモ的な記事です。
CompanionDeviceManagerの仕組み
CDM にデバイスを登録(association)して startObservingDevicePresence を呼ぶと、OS がデバイスの BLE 信号を代わりに監視してくれます。デバイスが BLE 範囲に入るCompanionDeviceService が起動してコールバックが届きます。アプリのプロセスがキルされた状態でも動くのが最大のポイントです。
// association 作成後に presence 監視を開始
manager.startObservingDevicePresence(deviceMacAddress)
詰まったこと1
Android 16 で super を呼ぶと二重発火する
Android13〜15 は onDeviceAppeared / onDeviceDisappeared が呼ばれますが、Android 16 から onDevicePresenceEvent が追加されました。ここで
super.onDevicePresenceEvent(event) を呼ぶと、deprecated コールバックへ再ディスパッチするため両方のメソッドが呼ばれて二重処理になります。
@RequiresApi(Build.VERSION_CODES.BAKLAVA)
override fun onDevicePresenceEvent(event: DevicePresenceEvent) {
// super は意図的に呼ばない
val address = resolveAssociationAddress(event.associationId)
when (event.event) {
DevicePresenceEvent.EVENT_BLE_APPEARED -> handleDeviceAppeared(address) DevicePresenceEvent.EVENT_BLE_DISAPPEARED -> handleDeviceDisappeared(address)
}
}
また Android 16 では startObservingDevicePresence(String) も deprecated になり、ObservingDevicePresenceRequest を使う新しい API に変わっています。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
manager.startObservingDevicePresence(
ObservingDevicePresenceRequest.Builder().setAssociationId(association.id).build()
)
} else {
@Suppress("DEPRECATION")
manager.startObservingDevicePresence(deviceMacAddress)
}
詰まったこと2
プロセス再生成後に監視が消える
ペアリング時に startObservingDevicePresence を呼んだだけでは、アプリプロセスが再生成されたあと監視が失われていました。Application.onCreate で既存の association に対して監視を再登録する必要があります。
// Application.onCreate で呼ぶ
fun reobserveAssociatedDevices() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
manager.myAssociations.forEach { association ->
startObservingDevicePresence(manager, association)
}
}
この処理を入れるまで、アプリを一度強制終了するとデバイスが来ても同期が走らない問題に悩まされていました。