Il primo passo per interagire con un dispositivo BLE è connettersi. Più
in particolare, la connessione al server GATT sul dispositivo. Per connetterti a un server GATT su un dispositivo BLE, utilizza il metodo connectGatt(). Questo metodo accetta tre parametri: un oggetto
Context, autoConnect (un valore booleano
che indica se connettersi automaticamente al dispositivo BLE non appena
diventa disponibile) e un riferimento a un
BluetoothGattCallback:
var bluetoothGatt: BluetoothGatt? = null // ... bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
Si connette al server GATT ospitato dal dispositivo BLE e restituisce un'istanza di BluetoothGatt, che puoi utilizzare per eseguire operazioni del client GATT. Il chiamante (l'app per Android)
è il client GATT. BluetoothGattCallback viene utilizzato per fornire risultati al client, ad esempio lo stato della connessione, nonché eventuali ulteriori operazioni del client GATT.
Configurare un servizio associato
Nell'esempio seguente, l'app BLE fornisce un'attività
(DeviceControlActivity) per connettersi ai dispositivi Bluetooth, visualizzare i dati del dispositivo
e visualizzare i servizi e le caratteristiche GATT supportati dal dispositivo. In base
all'input dell'utente, questa attività comunica con un
Service chiamato BluetoothLeService, che
interagisce con il dispositivo BLE tramite l'API BLE. La comunicazione viene
eseguita utilizzando un servizio associato che consente
all'attività di connettersi a BluetoothLeService e chiamare funzioni per
connettersi ai dispositivi. BluetoothLeService richiede un'implementazione di
Binder che fornisca l'accesso
al servizio per l'attività.
class BluetoothLeService : Service() { private val binder = LocalBinder() override fun onBind(intent: Intent): IBinder? { return binder } inner class LocalBinder : Binder() { fun getService(): BluetoothLeService { return this@BluetoothLeService } } }
L'attività può avviare il servizio utilizzando
bindService(),
passando un Intent per avviare il
servizio, un'implementazione di ServiceConnection
per ascoltare gli eventi di connessione e disconnessione e un flag
per specificare opzioni di connessione aggiuntive.
class DeviceControlActivity : AppCompatActivity() { private var bluetoothService: BluetoothLeService? = null // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> // call functions on service to check connection and connect to devices } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.gatt_services_characteristics) val gattServiceIntent = Intent(this, BluetoothLeService::class.java) bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) } }
Configurare BluetoothAdapter
Una volta associato, il servizio deve accedere a
BluetoothAdapter. Dovrebbe
verificare che l'adattatore sia disponibile sul dispositivo. Per saperne di più su BluetoothAdapter, consulta Configurare il Bluetooth. L'esempio seguente racchiude questo codice di configurazione in una funzione
initialize() che restituisce un valore Boolean che indica l'esito positivo.
private const val TAG = "BluetoothLeService" class BluetoothLeService : Service() { private var bluetoothAdapter: BluetoothAdapter? = null fun initialize(): Boolean { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() if (bluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter.") return false } return true } // ... }
L'attività chiama questa funzione all'interno della sua implementazione ServiceConnection.
La gestione di un valore restituito false dalla funzione initialize() dipende dalla tua
applicazione. Potresti mostrare all'utente un messaggio di errore che indica che il
dispositivo attuale non supporta l'operazione Bluetooth o disattivare qualsiasi funzionalità
che richiede il funzionamento del Bluetooth. Nell'esempio seguente,
finish() viene chiamato nell'attività
per riportare l'utente alla schermata precedente.
class DeviceControlActivity : AppCompatActivity() { // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() } // perform device connection } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } } // ... }
Collega a un dispositivo
Una volta inizializzata l'istanza BluetoothLeService, può connettersi al dispositivo BLE. L'attività deve inviare l'indirizzo del dispositivo al servizio in modo che possa
avviare la connessione. Il servizio chiamerà prima
getRemoteDevice()
sul BluetoothAdapter per accedere al dispositivo. Se l'adattatore non riesce a trovare
un dispositivo con quell'indirizzo, getRemoteDevice() genera un
IllegalArgumentException.
fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address.") return false } // connect to the GATT server on the device return true } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } }
DeviceControlActivity chiama questa funzione connect() una volta inizializzato il servizio. L'attività deve passare l'indirizzo del dispositivo BLE. Nell'esempio
seguente, l'indirizzo del dispositivo viene passato all'attività come extra dell'intent.
// Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() } // perform device connection deviceAddress?.let { bluetooth.connect(it) } } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } }
Dichiarare il callback GATT
Una volta che l'attività comunica al servizio a quale dispositivo connettersi e il servizio si connette al dispositivo, il servizio deve connettersi al server GATT sul dispositivo BLE. Questa connessione richiede un BluetoothGattCallback per ricevere
notifiche sullo stato della connessione, sul rilevamento del servizio, sulle letture delle caratteristiche e sulle notifiche delle caratteristiche.
Questo argomento si concentra sulle notifiche relative allo stato della connessione. Consulta Trasferire dati BLE per scoprire come eseguire l'individuazione del servizio, le letture delle caratteristiche e le richieste di notifiche delle caratteristiche.
La funzione
onConnectionStateChange()
viene attivata quando cambia la connessione al server GATT del dispositivo.
Nell'esempio seguente, il callback è definito nella classe Service, quindi
può essere utilizzato con
BluetoothDevice una volta che il
servizio si connette.
private val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server } } }
Connettiti al servizio GATT
Una volta dichiarato BluetoothGattCallback, il servizio può utilizzare l'oggetto
BluetoothDevice della funzione connect() per connettersi al servizio GATT
sul dispositivo.
Viene utilizzata la funzione
connectGatt(). A questo scopo sono necessari un oggetto Context, un flag booleano autoConnect e BluetoothGattCallback. In questo esempio, l'app si connette
direttamente al dispositivo BLE, quindi false viene passato per autoConnect.
Viene aggiunta anche una proprietà BluetoothGatt. In questo modo, il servizio può chiudere la
connessione quando non è
più necessaria.
class BluetoothLeService : Service() { // ... private var bluetoothGatt: BluetoothGatt? = null // ... fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) // connect to the GATT server on the device bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) return true } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address. Unable to connect.") return false } } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } } }
Aggiornamenti sulle trasmissioni
Quando il server si connette o si disconnette dal server GATT, deve notificare l'attività del nuovo stato. Esistono diversi modi per raggiungere questo obiettivo. L'esempio seguente utilizza le trasmissioni per inviare le informazioni dal servizio all'attività.
Il servizio dichiara una funzione per trasmettere il nuovo stato. Questa funzione accetta
una stringa di azione che viene passata a un oggetto Intent prima di essere trasmessa
al sistema.
private fun broadcastUpdate(action: String) { val intent = Intent(action) sendBroadcast(intent) }
Una volta implementata, la funzione di trasmissione viene utilizzata all'interno di
BluetoothGattCallback per inviare informazioni sullo stato della connessione con il
server GATT. Le costanti e lo stato attuale della connessione del servizio vengono dichiarati
nel servizio che rappresenta le azioni Intent.
class BluetoothLeService : Service() { private var connectionState = STATE_DISCONNECTED private val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server connectionState = STATE_CONNECTED broadcastUpdate(ACTION_GATT_CONNECTED) } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server connectionState = STATE_DISCONNECTED broadcastUpdate(ACTION_GATT_DISCONNECTED) } } } // ... companion object { const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED" const val ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" private const val STATE_DISCONNECTED = 0 private const val STATE_CONNECTED = 2 } }
Ascoltare gli aggiornamenti dell'attività
Una volta che il servizio trasmette gli aggiornamenti della connessione, l'attività deve
implementare un BroadcastReceiver.
Registra questo ricevitore durante la configurazione dell'attività e annullane la registrazione quando
l'attività esce dallo schermo. Ascoltando gli eventi del servizio, l'attività è in grado di aggiornare l'interfaccia utente in base allo stato attuale della connessione con il dispositivo BLE.
class DeviceControlActivity : AppCompatActivity() { private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { BluetoothLeService.ACTION_GATT_CONNECTED -> { connected = true updateConnectionState(R.string.connected) } BluetoothLeService.ACTION_GATT_DISCONNECTED -> { connected = false updateConnectionState(R.string.disconnected) } } } } override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) bluetoothService?.let { service -> deviceAddress?.let { address -> val result = service.connect(address) Log.d(TAG, "Connect request result=$result") } } } override fun onPause() { super.onPause() unregisterReceiver(gattUpdateReceiver) } private fun makeGattUpdateIntentFilter(): IntentFilter { return IntentFilter().apply { addAction(BluetoothLeService.ACTION_GATT_CONNECTED) addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) } } }
In Trasferisci dati BLE, il BroadcastReceiver viene utilizzato anche per comunicare l'individuazione del servizio e i dati delle caratteristiche del dispositivo.
Chiudi connessione GATT
Un passaggio importante quando si gestiscono le connessioni Bluetooth è chiuderle
quando non ti servono più. Per farlo, chiama la funzione close()
sull'oggetto BluetoothGatt. Nell'esempio seguente, il servizio
contiene il riferimento a BluetoothGatt. Quando l'attività viene scollegata dal
servizio, la connessione viene chiusa per evitare di scaricare la batteria del dispositivo.
class BluetoothLeService : Service() { // ... override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } private fun close() { bluetoothGatt?.let { gatt -> gatt.close() bluetoothGatt = null } } }