Połącz się z serwerem GATT

Pierwszym krokiem podczas interakcji z urządzeniem BLE jest nawiązanie z nim połączenia. Więcej a zwłaszcza z serwerem GATT na urządzeniu. Łączenie z GATT na urządzeniu BLE, użyj connectGatt() . Przyjmuje ona 3 parametry: Context obiekt autoConnect (wartość logiczna) wskazujący, czy automatycznie połączyć się z urządzeniem BLE, gdy tylko ) oraz odniesienie do BluetoothGattCallback:

Kotlin

var bluetoothGatt: BluetoothGatt? = null
...

bluetoothGatt = device.connectGatt(this, false, gattCallback)

Java

bluetoothGatt = device.connectGatt(this, false, gattCallback);

Łączy się z serwerem GATT hostowanym przez urządzenie BLE i zwraca BluetoothGatt, czyli za pomocą których możesz przeprowadzać operacje klienta w GATT. Rozmówca (aplikacja na Androida) jest klient GATT. Usługa BluetoothGattCallback jest używana do dostarczania klientowi wyników, takich jak: stanu połączenia oraz innych operacji na kliencie GATT.

Konfigurowanie powiązanej usługi

W poniższym przykładzie aplikacja BLE udostępnia aktywność (DeviceControlActivity) aby połączyć się z urządzeniami Bluetooth, wyświetlić dane z urządzenia, oraz wyświetlać usługi i cechy GATT obsługiwane przez urządzenie. Siedziba na dane wejściowe użytkownika, aktywność ta komunikuje się z Service o nazwie BluetoothLeService, która współpracuje z urządzeniem BLE przez interfejs BLE API. Komunikacja jest wykonane przy użyciu powiązanej usługi, która umożliwia działanie związane z łączeniem się z obiektem BluetoothLeService i wywoływaniem funkcji, połączyć urządzenia. BluetoothLeService wymaga: Implementacja Binder, która zapewnia dostęp do i usługi związane z aktywnością.

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 uruchomić usługę za pomocą bindService() z wynikiem Intent, aby rozpocząć usługa, ServiceConnection wdrożenia, aby nasłuchiwać zdarzeń połączenia i rozłączenia, a także flaga aby określić 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 adapter Bluetooth

Powiązanie z usługą wymaga dostępu do BluetoothAdapter Powinna Sprawdź, czy w urządzeniu jest dostępna przejściówka. Przeczytaj sekcję Konfiguracja Bluetooth, by dowiedzieć się więcej BluetoothAdapter. W poniższym przykładzie kod konfiguracji umieszcza się w Funkcja initialize(), która zwraca wartość Boolean wskazującą powodzenie.

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;
    }

    ...
}

Ta aktywność wywołuje tę funkcję w ramach implementacji ServiceConnection. Obsługa wartości zwróconej 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 funkcji Bluetooth lub wyłącz żadne funkcje które wymagają Bluetootha do działania. W poniższym przykładzie Metoda finish() zostaje wywołana dla aktywności aby przenieść użytkownika z powrotem 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 instancji BluetoothLeService może się ona połączyć z BLE urządzenia. Aktywność musi wysłać adres urządzenia do usługi, aby mogła zainicjuj 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 z tym adresem, getRemoteDevice() przesyła 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
}

Funkcja DeviceControlActivity wywołuje funkcję connect(), gdy usługa jest zainicjowano. Aktywność musi być przekazywana w adresie urządzenia BLE. W W tym przykładzie adres urządzenia jest przekazywany do aktywności jako intencja dodatkowe.

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;
    }
};

Zadeklarowanie wywołania zwrotnego GATT

Gdy aktywność poinformuje usługę, z którym urządzeniem i usługą się połączyć jeśli połączy się z urządzeniem, usługa musi połączyć się z serwerem GATT urządzenie BLE. To połączenie wymaga BluetoothGattCallback, aby odbierać wiadomości powiadomienia o stanie połączenia, wykrywanie usług, charakterystyka odczyty i powiadomienia o charakterystycznych cechach.

Ten temat dotyczy powiadomień o stanie połączenia. Zobacz Przenoszenie BLE , aby dowiedzieć się, jak osiągnąć skuteczność wykrywanie usług, odczyty cech i charakterystyki żądań powiadomienia.

onConnectionStateChange() jest wyzwalana po zmianie połączenia z serwerem GATT urządzenia. W poniższym przykładzie wywołanie zwrotne jest zdefiniowane w klasie Service, więc można używać z BluetoothDevice raz z inną usługą.

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 obiektu BluetoothGattCallback usługa może używać parametru obiekt BluetoothDevice z funkcji connect(), który ma zostać połączony z GATT. usłudze dostępnej na urządzeniu.

connectGatt() . Wymaga to obiektu Context i wartości logicznej autoConnect flaga i BluetoothGattCallback. W tym przykładzie aplikacja jest bezpośrednio nawiązywane jest połączenie z urządzeniem BLE, więc sygnał false jest przekazywany dla autoConnect.

Dodano także właściwość BluetoothGatt. Umożliwi to usłudze zamknięcie , gdy nie jest jest potrzebna dłużej.

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 BluetoothLeService 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 powiadomić o tym działania nowego stanu. Istnieje kilka sposobów wykonania tej czynności. poniższy przykład wykorzystuje transmisje do wysłania informacji między usługą a aktywnością.

Usługa deklaruje funkcję rozgłaszającą nowy stan. Ta funkcja wykonuje w ciągu działania, który jest przekazywany do obiektu Intent przed transmisją 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 włączeniu funkcji transmisji jest ona wykorzystywana w BluetoothGattCallback, aby wysyłać informacje o stanie połączenia z Serwer GATT. Deklarowane są stałe i bieżący stan połączenia usługi w usłudze reprezentującej działania Intent.

Kotlin

class BluetoothLeService : 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 BluetoothLeService 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);
            }
        }
    };

    …
}

Wykrywaj aktualizacje dotyczące aktywności

Gdy usługa przekaże aktualizacje połączenia, aktywność musi wdrożyć element BroadcastReceiver. Zarejestruj tego odbiorcę podczas konfigurowania aktywności i wyrejestruj go, gdy Aktywność opuszcza ekran. Nasłuchując zdarzeń z usługi, działanie może zaktualizować interfejs użytkownika 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 sekcji Przenieś dane BLE: BroadcastReceiver służy też do przekazywania wykrywania usług jako a także dane charakterystyczne urządzenia.

Zamknij połączenie GATT

Ważnym krokiem podczas obsługi połączeń Bluetooth jest zamknięcie gdy skończysz korzystać z internetu. Aby to zrobić, wywołaj close() na obiekcie BluetoothGatt. W poniższym przykładzie usługa zawiera odniesienie do elementu BluetoothGatt. Po usunięciu powiązania aktywności z , połączenie zostaje zamknięte, 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 BluetoothLeService extends Service {

...

      @Override
      public boolean onUnbind(Intent intent) {
          close();
          return super.onUnbind(intent);
      }

      private void close() {
          if (bluetoothGatt == null) {
              Return;
          }
          bluetoothGatt.close();
          bluetoothGatt = null;
      }
}