Il primo passaggio per interagire con un dispositivo BLE è connettersi al dispositivo. Nello specifico, la connessione al server GATT sul dispositivo. Per connetterti a un server GATT su un dispositivo BLE, utilizza il metodo connectGatt()
. Questo metodo richiede 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 BluetoothGattCallback
:
Kotlin
var bluetoothGatt: BluetoothGatt? = null ... bluetoothGatt = device.connectGatt(this, false, gattCallback)
Java
bluetoothGatt = device.connectGatt(this, false, gattCallback);
Questa operazione si connette al server GATT ospitato dal dispositivo BLE e restituisce un'istanza BluetoothGatt
, che puoi utilizzare per eseguire le operazioni del client GATT. Il chiamante (l'app Android)
è il client GATT. BluetoothGattCallback
viene utilizzato per inviare risultati al client, come lo stato della connessione e qualsiasi ulteriore operazione del client GATT.
Configura 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 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 di chiamare le funzioni per la connessione ai dispositivi. BluetoothLeService
richiede un'implementazione
Binder
che fornisca l'accesso al
servizio per l'attività.
Kotlin
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 } } }
Java
class BluetoothLeService extends Service { private Binder binder = new LocalBinder(); @Nullable @Override public IBinder onBind(Intent intent) { return binder; } class LocalBinder extends Binder { public BluetoothLeService getService() { return BluetoothLeService.this; } } }
L'attività può avviare il servizio utilizzando bindService()
, trasmettendo un elemento Intent
per avviare il servizio, un'implementazione di ServiceConnection
per l'ascolto degli eventi di connessione e disconnessione e un flag per specificare ulteriori opzioni di connessione.
Kotlin
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) } }
Java
class DeviceControlActivity extends AppCompatActivity { private BluetoothLeService bluetoothService; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { // call functions on service to check connection and connect to devices } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.gatt_services_characteristics); Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } }
Configurazione dell'adattatore Bluetooth
Una volta associato il servizio, deve accedere a BluetoothAdapter
. Deve controllare che l'adattatore sia disponibile sul dispositivo. Leggi Configurare
il Bluetooth per ulteriori informazioni sulla
BluetoothAdapter
. L'esempio seguente aggrega questo codice di configurazione in una funzione initialize()
che restituisce un valore Boolean
che indica l'esito positivo.
Kotlin
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 } ... }
Java
class BluetoothLeService extends Service { public static final String TAG = "BluetoothLeService"; private BluetoothAdapter bluetoothAdapter; public boolean initialize() { 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 falso valore restituito dalla funzione initialize()
dipende dall'applicazione. Potresti mostrare un messaggio di errore all'utente che indica che il dispositivo corrente non supporta l'operazione Bluetooth o disattiva funzionalità che richiedono il Bluetooth per il funzionamento. Nell'esempio seguente, finish()
viene richiamato sull'attività per rimandare l'utente alla schermata precedente.
Kotlin
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 } } ... }
Java
class DeviceControlsActivity extends AppCompatActivity { private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } // perform device connection } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; ... }
Collega a un dispositivo
Dopo aver inizializzato BluetoothService
, 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
.
Kotlin
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 } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } }
Java
public boolean connect(final String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } try { final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); } catch (IllegalArgumentException exception) { Log.w(TAG, "Device not found with provided address."); return false; } // connect to the GATT server on the device }
DeviceControlActivity
chiama questa funzione connect()
dopo l'inizializzazione del servizio. L'attività deve essere trasmessa nell'indirizzo del dispositivo BLE. Nell'esempio seguente, l'indirizzo del dispositivo viene passato all'attività come intent aggiuntivo.
Kotlin
// 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 bluetooth.connect(deviceAddress) } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } }
Java
private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } // perform device connection bluetoothService.connect(deviceAddress); } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } };
Dichiara callback GATT
Dopo che l'attività indica al servizio a quale dispositivo connettersi e che 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 relative alle caratteristiche.
Questo argomento è incentrato sulle notifiche relative allo stato della connessione. Consulta Trasferire dati BLE per informazioni su come eseguire il rilevamento dei servizi, le letture delle caratteristiche e la richiesta di notifiche relative alle caratteristiche.
La funzione onConnectionStateChange()
viene attivata quando la connessione al server GATT del dispositivo cambia.
Nell'esempio seguente, il callback è definito nella classe Service
, quindi
può essere utilizzato con
BluetoothDevice
dopo la connessione
del servizio.
Kotlin
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 } } }
Java
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 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()
. Richiede un oggetto Context
, un flag booleano autoConnect
e BluetoothGattCallback
. In questo esempio, l'app si connette direttamente al dispositivo BLE, quindi false
viene trasmesso per autoConnect
.
Viene aggiunta anche una proprietà BluetoothGatt
. In questo modo il servizio può chiudere la connessione quando non è più necessaria.
Kotlin
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 } } }
Java
class BluetoothService extends Service { ... private BluetoothGatt bluetoothGatt; ... public boolean connect(final String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } try { final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); // connect to the GATT server on the device bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); return true; } catch (IllegalArgumentException exception) { Log.w(TAG, "Device not found with provided address. Unable to connect."); return false; } } }
Trasmetti aggiornamenti
Quando il server si connette o disconnette dal server GATT, deve notificare all'attività il nuovo stato. Esistono diversi modi per eseguire questa operazione. 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.
Kotlin
private fun broadcastUpdate(action: String) { val intent = Intent(action) sendBroadcast(intent) }
Java
private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); }
Una volta attivata, 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 di connessione attuale del servizio vengono dichiarati nel servizio che rappresenta le azioni Intent
.
Kotlin
class BluetoothService : Service() { private var connectionState = STATE_DISCONNECTED private val bluetoothGattCallback: 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 } }
Java
class BluetoothService extends Service { public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTED = 2; private int connectionState; ... private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 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); } } }; … }
Ascolta gli aggiornamenti nell'attività
Dopo che il servizio trasmette l'aggiornamento della connessione, l'attività deve implementare BroadcastReceiver
.
Registra questo ricevitore durante la configurazione dell'attività e annulla la registrazione quando l'attività esce dalla schermata. Se ascolti gli eventi dal servizio, l'attività può aggiornare l'interfaccia utente in base all'attuale stato della connessione con il dispositivo BLE.
Kotlin
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()) if (bluetoothService != null) { val result = bluetoothService!!.connect(deviceAddress) Log.d(DeviceControlsActivity.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) } } }
Java
class DeviceControlsActivity extends AppCompatActivity { ... private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { connected = true; updateConnectionState(R.string.connected); } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { connected = false; updateConnectionState(R.string.disconnected); } } }; @Override protected void onResume() { super.onResume(); registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()); if (bluetoothService != null) { final boolean result = bluetoothService.connect(deviceAddress); Log.d(TAG, "Connect request result=" + result); } } @Override protected void onPause() { super.onPause(); unregisterReceiver(gattUpdateReceiver); } private static IntentFilter makeGattUpdateIntentFilter() { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); return intentFilter; } }
In Trasferisci dati BLE, BroadcastReceiver
viene utilizzato anche per comunicare il rilevamento del servizio e i dati relativi alle caratteristiche del dispositivo.
Chiudi la connessione GATT
Un passaggio importante da eseguire nelle connessioni Bluetooth
è chiudere la connessione quando hai finito di usarla. Per farlo, chiama la funzione close()
nell'oggetto BluetoothGatt
. Nell'esempio seguente, il servizio contiene il riferimento a BluetoothGatt
. Quando l'attività viene svincolata dal servizio, la connessione viene chiusa per evitare di consumare la batteria del dispositivo.
Kotlin
class BluetoothLeService : Service() { ... override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } private fun close() { bluetoothGatt?.let { gatt -> gatt.close() bluetoothGatt = null } } }
Java
class BluetoothService extends Service { ... @Override public boolean onUnbind(Intent intent) { close(); return super.onUnbind(intent); } private void close() { if (bluetoothGatt == null) { Return; } bluetoothGatt.close(); bluetoothGatt = null; } }