Verbindung zu einem GATT-Server herstellen

Der erste Schritt bei der Interaktion mit einem BLE-Gerät besteht darin, eine Verbindung zu diesem Gerät herzustellen. Mehr eine Verbindung zum GATT-Server auf dem Gerät herstellen. Verbindung zu einem GATT herstellen Server auf einem BLE-Gerät verwenden, nutzen Sie connectGatt() . Diese Methode verwendet drei Parameter: a Context-Objekt, autoConnect (ein boolescher Wert gibt an, ob automatisch eine Verbindung zum BLE-Gerät hergestellt werden soll, sobald verfügbar wird) und ein Verweis auf eine BluetoothGattCallback:

Kotlin

var bluetoothGatt: BluetoothGatt? = null
...

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

Java

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

Dadurch wird eine Verbindung zum GATT-Server hergestellt, der vom BLE-Gerät gehostet wird, und eine Fehlermeldung BluetoothGatt-Instanz, die die Sie dann für die Durchführung von GATT-Client-Operationen verwenden können. Der Anrufer (Android-App) ist der GATT-Kunde. Die BluetoothGattCallback wird verwendet, um Ergebnisse an den Client zu liefern, z. B. Verbindungsstatus sowie alle weiteren GATT-Clientvorgänge.

gebundenen Dienst einrichten

Im folgenden Beispiel stellt die BLE-App eine Aktivität (DeviceControlActivity), um eine Verbindung zu Bluetooth-Geräten herzustellen, Gerätedaten anzuzeigen, und die vom Gerät unterstützten GATT-Dienste und -Eigenschaften aufrufen können. Basierend auf auf Nutzereingabe reagiert, kommuniziert diese Aktivität mit einem Service hat den Namen BluetoothLeService, mit dem interagiert über die BLE API mit dem BLE-Gerät. Die Kommunikation ist die mit einem gebundenen Dienst ausgeführt werden, die Aktivität, um eine Verbindung zu BluetoothLeService herzustellen und Funktionen aufzurufen, eine Verbindung zu den Geräten herstellen. Für BluetoothLeService ist ein Binder-Implementierung, die Zugriff auf den Dienst für die Aktivität.

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

Die Aktivität kann den Dienst starten mit bindService(), übergeben eine Intent, um die Dienst, ein ServiceConnection -Implementierung zum Warten auf Verbindungs- und Trennungsereignisse sowie ein Flag um zusätzliche Verbindungsoptionen anzugeben.

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

Bluetooth-Adapter einrichten

Sobald der Dienst an ihn gebunden ist, muss er auf den BluetoothAdapter Er sollte Prüfen Sie, ob der Adapter auf dem Gerät verfügbar ist. Weitere Informationen finden Sie unter Einrichtung Bluetooth BluetoothAdapter. Im folgenden Beispiel ist dieser Einrichtungscode initialize()-Funktion, die einen Boolean-Wert zurückgibt, der einen Erfolg anzeigt.

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

    ...
}

Die Aktivität ruft diese Funktion innerhalb ihrer ServiceConnection-Implementierung auf. Ob der Rückgabewert der Funktion initialize() falsch ist, hängt davon ab, . Sie könnten dem Nutzer eine Fehlermeldung anzeigen, die besagt, dass die Das aktuelle Gerät unterstützt den Bluetooth-Betrieb nicht oder deaktiviert irgendwelche Funktionen die Bluetooth erfordern. Im folgenden Beispiel finish() wird bei der Aktivität aufgerufen um den Nutzer zum vorherigen Bildschirm zu wechseln.

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

    ...
}

Mit einem Gerät verbinden

Sobald die BluetoothLeService-Instanz initialisiert ist, kann sie eine Verbindung zu BLE herstellen . Die Aktivität muss die Geräteadresse an den Dienst senden, damit er Initiieren Sie die Verbindung. Der Dienst ruft zuerst auf getRemoteDevice() auf dem BluetoothAdapter, um auf das Gerät zuzugreifen. Wenn der Adapter den Gerät mit dieser Adresse wird, gibt getRemoteDevice() den Fehler 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 ruft diese connect()-Funktion auf, sobald der Dienst initialisiert. Die Aktivität muss die Adresse des BLE-Geräts übergeben. In Im folgenden Beispiel wird die Geräteadresse als Intent extra.

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

GATT-Callback deklarieren

Sobald dem Dienst mitgeteilt wurde, mit welchem Gerät und zu welchem Dienst eine Verbindung zum Gerät herstellt, muss der Dienst eine Verbindung zum GATT-Server BLE-Gerät. Für diese Verbindung ist ein BluetoothGattCallback erforderlich Benachrichtigungen über Verbindungsstatus, Diensterkennung, Merkmal gelesenen und charakteristischen Benachrichtigungen.

In diesem Thema geht es um Benachrichtigungen zum Verbindungsstatus. Siehe Übertragung von BLE Daten für die Leistung Diensterkennung, charakteristische Lesevorgänge und Anfragemerkmal Benachrichtigungen.

Die onConnectionStateChange() wird ausgelöst, wenn sich die Verbindung zum GATT-Server des Geräts ändert. Im folgenden Beispiel ist der Callback in der Klasse Service definiert, sodass er kann mit der BluetoothDevice, sobald das eine Verbindung zu ihr herstellt.

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

Mit GATT-Dienst verbinden

Sobald BluetoothGattCallback deklariert ist, kann der Dienst die Methode BluetoothDevice-Objekt aus der Funktion connect(), um eine Verbindung zum GATT herzustellen auf dem Gerät.

Die connectGatt() -Funktion verwendet wird. Hierfür ist ein Context-Objekt und ein boolescher Wert (autoConnect) erforderlich und dem BluetoothGattCallback. In diesem Beispiel ist die App direkt Die Verbindung zum BLE-Gerät wird hergestellt. Daher wird false für autoConnect übergeben.

Außerdem wird die Property BluetoothGatt hinzugefügt. Dadurch kann der Dienst die Verbindung, wenn keine Verbindung besteht nicht mehr benötigt wird.

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

Broadcast-Updates

Wenn der Server eine Verbindung zum GATT-Server herstellt oder trennt, muss er benachrichtigt werden, die Aktivität des neuen Status. Dafür gibt es mehrere Möglichkeiten. Die Im folgenden Beispiel werden broadcasts zum Senden der vom Dienst bis zur Aktivität.

Der Dienst deklariert eine Funktion zur Übertragung des neuen Status. Diese Funktion nimmt in einem Aktionsstring, der vor der Übertragung an ein Intent-Objekt übergeben wird an das System gesendet werden.

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

Sobald die Broadcast-Funktion eingerichtet ist, wird sie im BluetoothGattCallback, um Informationen zum Verbindungsstatus mit dem GATT-Server. Konstanten und der aktuelle Verbindungsstatus des Dienstes werden deklariert in dem Dienst, der die Intent-Aktionen darstellt.

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

    
}

In Aktivitäten auf Updates warten

Sobald der Dienst die Verbindungsaktualisierungen sendet, muss die Aktivität Implementiere eine BroadcastReceiver. Registrieren Sie diesen Empfänger beim Einrichten der Aktivität und heben Sie die Registrierung auf, wenn das wenn eine Aktivität den Bildschirm verlässt. Durch das Warten auf Ereignisse vom Dienst Die Aktivität kann die Benutzeroberfläche auf Grundlage der aktuellen Verbindungsstatus mit dem BLE-Gerät.

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

Gehen Sie unter BLE-Daten übertragen so vor: Das BroadcastReceiver wird auch verwendet, um die Diensterkennung als sowie die charakteristischen Daten des Geräts.

GATT-Verbindung trennen

Ein wichtiger Schritt bei Bluetooth-Verbindungen ist das Schließen der wenn Sie fertig sind. Rufen Sie dazu close() auf. für das BluetoothGatt-Objekt. Im folgenden Beispiel hat der Dienst enthält den Verweis auf das BluetoothGatt. Wenn die Aktivität von der wird die Verbindung getrennt, damit der Geräteakku nicht belastet wird.

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