Pierwszym krokiem w interakcji z urządzeniem BLE jest nawiązanie do niego połączenia. Chodzi konkretnie o łączenie się z serwerem GATT na urządzeniu. Aby połączyć się z serwerem GATT na urządzeniu BLE, użyj metody connectGatt()
. Ta metoda przyjmuje 3 parametry: obiekt Context
, autoConnect
(wartość logiczna wskazująca, czy należy automatycznie połączyć się z urządzeniem BLE, gdy tylko stanie się dostępne) oraz odwołanie do BluetoothGattCallback
:
Kotlin
var bluetoothGatt: BluetoothGatt? = null ... bluetoothGatt = device.connectGatt(this, false, gattCallback)
Java
bluetoothGatt = device.connectGatt(this, false, gattCallback);
Łączy się to z serwerem GATT hostowanym przez urządzenie BLE i zwraca instancję BluetoothGatt
, której możesz użyć do wykonywania operacji klienta GATT. Element wywołujący (aplikacja na Androida)
to klient GATT. Pole BluetoothGattCallback
służy do dostarczania klientowi wyników, takich jak stan połączenia, oraz wszelkich dalszych operacji klienckich w GATT.
Skonfiguruj powiązaną usługę
W poniższym przykładzie aplikacja BLE udostępnia działanie (DeviceControlActivity
) umożliwiające połączenie z urządzeniami Bluetooth, wyświetlanie danych z urządzenia oraz wyświetlanie usług i parametrów GATT obsługiwanych przez urządzenie. Na podstawie danych wejściowych użytkownika ta aktywność komunikuje się z obiektem Service
o nazwie BluetoothLeService
, który wchodzi w interakcję z urządzeniem BLE przez interfejs BLE API. Komunikacja jest realizowana za pomocą powiązanej usługi, która umożliwia aktywności na nawiązanie połączenia z interfejsem BluetoothLeService
i wywołanie funkcji w celu połączenia się z urządzeniami. Element BluetoothLeService
wymaga implementacji Binder
, która zapewnia dostęp do usługi w ramach tego działania.
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; } } }
Działanie może uruchamiać usługę za pomocą kodu bindService()
, który może przekazywać kod Intent
w celu uruchomienia usługi, implementacji ServiceConnection
do nasłuchiwania zdarzeń połączenia i rozłączenia oraz flagi określającej dodatkowe opcje połączenia.
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); } }
Skonfiguruj BluetoothAdapter
Po powiązaniu usługa musi mieć dostęp do BluetoothAdapter
. Sprawdź, czy przejściówka jest dostępna na urządzeniu. Przeczytaj Skonfiguruj
Bluetooth, aby dowiedzieć się więcej
na temat BluetoothAdapter
. W poniższym przykładzie ten kod konfiguracji jest ujęty w funkcję initialize()
, która zwraca wartość Boolean
wskazującą na sukces.
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; } ... }
Aktywność wywołuje tę funkcję w ramach swojej implementacji ServiceConnection
.
Obsługa fałszywej wartości zwracanej przez funkcję initialize()
zależy od aplikacji. Możesz wyświetlić użytkownikowi komunikat o błędzie informujący, że obecne urządzenie nie obsługuje operacji Bluetooth lub wyłączyć funkcje, które wymagają Bluetootha do działania. W poniższym przykładzie działanie finish()
jest wywoływane przez działanie w celu przekierowania użytkownika na poprzedni ekran.
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; } }; ... }
Połącz się z urządzeniem
Po zainicjowaniu BluetoothService
może połączyć się z urządzeniem BLE.
Aktywność musi wysłać adres urządzenia do usługi, aby mogło zainicjować połączenie. Usługa najpierw wywoła metodę getRemoteDevice()
na urządzeniu BluetoothAdapter
, aby uzyskać dostęp do urządzenia. Jeśli adapter nie może znaleźć urządzenia o tym adresie, getRemoteDevice()
zgłasza 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
wywołuje tę funkcję connect()
po zainicjowaniu usługi. Aktywność musi przekazywać adres urządzenia BLE. W poniższym przykładzie adres urządzenia jest przekazywany do działania jako dodatkowa intencja.
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; } };
Zadeklaruj wywołanie zwrotne GATT
Gdy aktywność informuje usługę, z którym urządzeniem ma się połączyć, a usługa łączy się z urządzeniem, usługa musi połączyć się z serwerem GATT na urządzeniu BLE. To połączenie wymaga BluetoothGattCallback
, aby otrzymywać powiadomienia o stanie połączenia, wykrywaniu usługi, przeczytaniu informacji o cechach i powiadomieniach o charakterze.
Ten temat dotyczy powiadomień o stanie połączenia. Informacje o tym, jak wykrywać usługi, odczytywać dane i prosić o powiadomienia o cechach, znajdziesz w sekcji Przesyłanie danych BLE.
Funkcja onConnectionStateChange()
jest aktywowana po zmianie połączenia z serwerem GATT na urządzeniu.
W poniższym przykładzie wywołanie zwrotne jest zdefiniowane w klasie Service
, więc można go używać z BluetoothDevice
, gdy usługa się z nim połączy.
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 } } };
Połącz z usługą GATT
Po zadeklarowaniu BluetoothGattCallback
usługa może używać obiektu BluetoothDevice
z funkcji connect()
, aby łączyć się z usługą GATT na urządzeniu.
Używana jest funkcja connectGatt()
. Wymaga to obiektu Context
, flagi wartości logicznej autoConnect
i BluetoothGattCallback
. W tym przykładzie aplikacja łączy się bezpośrednio z urządzeniem BLE, więc przekazywana jest metoda false
dla autoConnect
.
Zostanie też dodana właściwość BluetoothGatt
. Dzięki temu usługa może zamknąć połączenie, gdy nie będzie już potrzebne.
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; } } }
Aktualne informacje o transmisjach
Gdy serwer łączy się z serwerem GATT lub się od niego rozłącza, musi powiadamiać o aktywności o nowym stanie. Można to osiągnąć na kilka sposobów. W tym przykładzie użyto transmisji do wysyłania informacji z usługi do działania.
Usługa deklaruje funkcję rozgłaszania nowego stanu. Ta funkcja pobiera ciąg znaków działania, który jest przekazywany do obiektu Intent
, zanim zostanie przesłany do systemu.
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); }
Po skonfigurowaniu funkcji przesyłania jest ona używana w obrębie BluetoothGattCallback
do wysyłania informacji o stanie połączenia do serwera GATT. Stałe i bieżący stan połączenia usługi są zadeklarowane w usłudze reprezentującej działania 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); } } }; … }
Wysłuchaj powiadomień dotyczących aktywności
Gdy usługa rozgłasza aktualizacje połączenia, działanie musi zaimplementować BroadcastReceiver
.
Zarejestruj ten odbiornik podczas konfigurowania aktywności i wyrejestruj go, gdy aktywność zniknie z ekranu. Nasłuchując zdarzeń z usługi, może ona aktualizować interfejs na podstawie bieżącego stanu połączenia z urządzeniem 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; } }
W przypadku przesyłania danych BLE obiekt BroadcastReceiver
służy też do przekazywania informacji o wykryciu usługi oraz danych charakterystycznych z urządzenia.
Zamknij połączenie z GATT
Ważnym etapem obsługi połączeń Bluetooth
jest zamknięcie połączenia na koniec. Aby to zrobić, wywołaj w obiekcie BluetoothGatt
funkcję close()
. W poniższym przykładzie usługa przechowuje odniesienie do obiektu BluetoothGatt
. Gdy aktywność usunie powiązanie z usługą, połączenie jest zamykane, aby uniknąć rozładowywania baterii urządzenia.
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; } }