This guide provides an overview of how to support key use cases for communicating with peripheral devices when your app is running in the background:
There are multiple options to support each of these use cases. Each one has benefits and drawbacks that might make it more or less suitable for your specific needs.
The following diagram shows a simplified view of the guidance on this page:
Find a device
First, your app needs to find a device to connect to. To find a BLE device you can use either of the following APIs:
BluetoothLeScanner
as described in Find BLE devices. (Sample)CompanionDeviceManager
as described in Companion device pairing. (Sample)
In the background
There is no limitation on using either of these APIs while the app is not visible, but they do both need your app process to be alive. If the app process is not running, you can use the following workarounds:
- For
BluetoothLeScanner
: CallstartScan()
with aPendingIntent
object instead of aScanCallback
object to get notified when a device matching your filter is scanned. (Sample) - For
CompanionDeviceManager
: Follow the guidance in Keep companion apps awake to wake the app and keep it awake while a previously associated device is in range. (Sample)
Connect to a device
To connect to a device after you have found it, you need to get a
BluetoothDevice
instance for
the device from one of the following sources:
- A
BluetoothLeScanner
scan result as described in the previous section. - The bonded device list retrieved from
BluetoothAdapter.getBondedDevices()
. - The
BluetoothAdapter
cache, usingBluetoothAdapter.getRemoteLeDevice()
.
After you have a BluetoothDevice
instance, you can start a connection request
to the corresponding device by calling one of the
connectGatt()
methods. The value that you pass into the autoConnect
boolean defines which of
the following two connection modes the GATT client uses:
- Direct connect (
autoconnect = false
): Try to connect to the peripheral device directly, and fail if the device isn't available. In case of disconnection, the GATT client doesn't automatically try to reconnect. - Auto connect (
autoconnect = true
): Try to automatically connect to the peripheral device whenever it's available. In case of a disconnection initiated by the peripheral or because the peripheral is out of range, the GATT client automatically tries to reconnect when the peripheral is available.
In the background
There is no restriction on connecting to a device while the app is in the background, although the connection is closed if your process is killed. In addition there are restrictions on starting activities (in Android 10 and higher) or foreground services (in Android 12 and higher) from the background.
Thus, to perform a connection while in the background, apps can use the following solutions:
- Use WorkManager to connect to
your device.
- You can set a
PeriodicWorkRequest
or aOneTimeWorkRequest
to perform a defined action, although app restrictions might apply. - Additionally, you can benefit from WorkManager features like work constraints, expedited work, retry policy, and more.
- If the connection needs to be kept alive as long as possible to perform a task, such as data syncing or polling from the peripheral devices, you need to start a foreground service following the guidance in Support for long-running workers. However, foreground service launch restrictions apply starting in Android 12.
- You can set a
- Start a foreground service with the
connectedDevice
type.- If the connection needs to be kept alive as long as possible to perform a task, such as data syncing or polling from the peripheral devices, you need to start a foreground service following the guidance in Support for long-running workers. However, foreground service launch restrictions apply starting in Android 12.
- Call
startScan()
with aPendingIntent
object as described in Find a device to wake your process up when the device is present. The peripheral device must be advertising.- We recommend that you start a Worker and a Job. This might be interrupted by the system and thus it can only support short duration communication.
- In versions lower than Android 12 you can start a foreground service
directly from the
PendingIntent
object.
- Use
CompanionDeviceService
and either of theREQUEST_COMPANION_RUN_IN_BACKGROUND
orREQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
permissions to start the service from the background.
Stay connected to a device
Ideally, apps should maintain connections to peripheral devices only as long as necessary, and disconnect once the task is completed. However, there are two cases where an app might need to keep a connection alive indefinitely:
In both cases, the following options are available:
- Use
CompanionDeviceService
with theREQUEST_COMPANION_RUN_IN_BACKGROUND
permission and theCompanionDeviceManager.startObservingDevicePresence()
method. - Start a foreground service while
the app is in the foreground (or within one of the
exemptions)
with the
connectedDevice
foreground type.
While switching between apps
Finding a device, connecting to it, and transferring data is time consuming and
resource intensive. To avoid losing connection and having to perform the full
process every time the user switches between apps or performs simultaneous
tasks, you should keep the connection alive until the operation finishes. You
can use either a foreground service with the connectedDevice
type or the
companion device presence
API.
While listening to peripheral notifications
To listen to peripheral notifications the app must call
setCharacteristicNotification()
,
listen to callbacks using
onCharacteristicChanged()
,
and keep the connection alive. For most apps, it's best to support this use case
with CompanionDeviceService
because the app will likely need to keep listening
for long periods of time. However, you can also use a foreground service.
In either case, you can reconnect after a terminated process by following the instructions in the Connect to a device section.