Bluetooth-Geräte verbinden

Um eine Verbindung zwischen zwei Geräten herzustellen, müssen Sie die beiden serverseitige und clientseitige Mechanismen, da ein Gerät einen Server öffnen muss und der andere muss die Verbindung über das MAC-Adresse Das Servergerät und das Clientgerät erhalten jeweils die erforderlichen BluetoothSocket in anderen . Der Server empfängt Socket-Informationen, wenn eine eingehende Verbindung akzeptiert. Der Client stellt Socket-Informationen bereit, wenn er einen RFCOMM-Kanal öffnet. mit dem Server.

Der Server und der Client gelten als miteinander verbunden, wenn sie jeweils ein verbundenes BluetoothSocket im selben RFCOMM-Kanal. An dieser Stelle kann das Gerät Ein- und Ausgabestreams abrufen und die Datenübertragung kann beginnen, wird im Abschnitt zur Übertragung von Bluetooth-Daten Daten. Dieser Abschnitt beschreibt, wie die Verbindung zwischen zwei Geräten hergestellt wird.

Stellen Sie sicher, dass Sie Bluetooth-Berechtigungen und Ihre App für Bluetooth einrichten, bevor Sie versuchen, Bluetooth-Geräte zu finden.

Verbindungstechniken

Eine Implementierungstechnik besteht darin, jedes Gerät automatisch als Server vorzubereiten. sodass jedes Gerät einen offenen ServerSocket hat und auf Verbindungen wartet. In In diesem Fall kann jedes Gerät eine Verbindung mit dem anderen herstellen und zum Client. Alternativ kann ein Gerät die Verbindung explizit hosten und ein und das andere Gerät initiiert die Verbindung.


Abbildung 1: Dialogfeld der Bluetooth-Kopplung

Als Server verbinden

Wenn Sie zwei Geräte verbinden möchten, muss eines davon als Server fungieren, indem ein öffnen BluetoothServerSocket Der Zweck des Server-Sockets besteht darin, auf eingehende Verbindungsanfragen zu warten. und eine verbundene BluetoothSocket angeben, nachdem eine Anfrage angenommen wurde. Wenn der Parameter BluetoothSocket wird von BluetoothServerSocket übernommen, dem BluetoothServerSocket kann und sollte verworfen werden, es sei denn, Sie möchten um mehr Verbindungen zu akzeptieren.

Um einen Server-Socket einzurichten und eine Verbindung zu akzeptieren, führen Sie die folgenden Schritte aus: Abfolge der Schritte:

  1. BluetoothServerSocket anrufen und erhalten listenUsingRfcommWithServiceRecord(String, UUID)

    Bei der Zeichenfolge handelt es sich um einen identifizierbaren Namen Ihres Dienstes, der vom System schreibt automatisch in einen neuen SDP-Datenbankeintrag (Service Discovery Protocol) auf dem Gerät. Der Name ist frei wählbar und kann einfach der Name Ihrer App sein. Der UUID (Universally Unique Identifier) ist ebenfalls im SDP-Eintrag enthalten und bildet die Grundlage für die Verbindungsvereinbarung mit dem Clientgerät. Das Wenn der Client versucht, eine Verbindung zu diesem Gerät herzustellen, trägt er eine UUID der den Dienst eindeutig identifiziert, mit dem eine Verbindung hergestellt werden soll. Diese Die UUIDs müssen übereinstimmen, damit die Verbindung akzeptiert wird.

    Eine UUID ist ein standardisiertes 128-Bit-Format für eine String-ID, das zur Identifizierung von Informationen. Mithilfe einer UUID werden Informationen identifiziert, in einem System oder Netzwerk eindeutig, da die Wahrscheinlichkeit, effektiv Null ist. Sie werden unabhängig und ohne Verwendung einer zentralisierten Behörde. In diesem Fall werden sie zur eindeutigen Identifizierung Ihrer Bluetooth-Dienst der App. Um eine UUID für Ihre App zu erhalten, können Sie diese verwenden der vielen zufälligen UUID-Generatoren im Web und initialisieren dann ein UUID mit fromString(String)

  2. Starten Sie das Warten auf Verbindungsanfragen, indem Sie Folgendes aufrufen: accept()

    Dieser Anruf wird blockiert. Sie wird zurückgegeben, wenn entweder eine Verbindung akzeptiert oder eine Ausnahme ist aufgetreten. Eine Verbindung wird nur akzeptiert, wenn ein Remote-Gerät hat eine Verbindungsanfrage mit einer UUID gesendet, die mit Der Socket, der für diesen Überwachungsserver-Socket registriert ist. Wenn der Vorgang erfolgreich war, accept() gibt eine verbundene BluetoothSocket zurück.

  3. Wenn Sie keine weiteren Verbindungen annehmen möchten, rufen Sie close()

    Dieser Methodenaufruf gibt den Server-Socket und alle zugehörigen Ressourcen frei, schließt die verbundene BluetoothSocket nicht, die von accept(). Anders als bei TCP/IP lässt RFCOMM nur einen verbundenen Client pro in den meisten Fällen, daher ist es in den meisten Fällen sinnvoll, close() auf der BluetoothServerSocket sofort nach Annahme eines verbundenen Sockets.

Da der accept()-Aufruf ein blockierender Aufruf ist, führen Sie ihn nicht im Hauptaufruf aus Aktivitäts-UI-Thread. Durch die Ausführung in einem anderen Thread wird sichergestellt, dass Ihre Anwendung dennoch auf andere Nutzerinteraktionen reagieren. Normalerweise ist es sinnvoll, mit einem BluetoothServerSocket oder BluetoothSocket in einem neuen Thread die von deiner App verwaltet werden. Rufe close() an, um einen blockierten Anruf wie accept() abzubrechen. zum BluetoothServerSocket oder BluetoothSocket aus einem anderen Thread. Hinweis dass alle Methoden für BluetoothServerSocket oder BluetoothSocket Thread-sicher.

Beispiel

Im Folgenden finden Sie einen vereinfachten Thread für die Serverkomponente, die eingehende Verbindungen:

Kotlin

private inner class AcceptThread : Thread() {

   private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
       bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
   }

   override fun run() {
       // Keep listening until exception occurs or a socket is returned.
       var shouldLoop = true
       while (shouldLoop) {
           val socket: BluetoothSocket? = try {
               mmServerSocket?.accept()
           } catch (e: IOException) {
               Log.e(TAG, "Socket's accept() method failed", e)
               shouldLoop = false
               null
           }
           socket?.also {
               manageMyConnectedSocket(it)
               mmServerSocket?.close()
               shouldLoop = false
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   fun cancel() {
       try {
           mmServerSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the connect socket", e)
       }
   }
}

Java

private class AcceptThread extends Thread {
   private final BluetoothServerSocket mmServerSocket;

   public AcceptThread() {
       // Use a temporary object that is later assigned to mmServerSocket
       // because mmServerSocket is final.
       BluetoothServerSocket tmp = null;
       try {
           // MY_UUID is the app's UUID string, also used by the client code.
           tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's listen() method failed", e);
       }
       mmServerSocket = tmp;
   }

   public void run() {
       BluetoothSocket socket = null;
       // Keep listening until exception occurs or a socket is returned.
       while (true) {
           try {
               socket = mmServerSocket.accept();
           } catch (IOException e) {
               Log.e(TAG, "Socket's accept() method failed", e);
               break;
           }

           if (socket != null) {
               // A connection was accepted. Perform work associated with
               // the connection in a separate thread.
               manageMyConnectedSocket(socket);
               mmServerSocket.close();
               break;
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   public void cancel() {
       try {
           mmServerSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the connect socket", e);
       }
   }
}

In diesem Beispiel ist nur eine eingehende Verbindung erwünscht. Wenn also akzeptiert und die BluetoothSocket abgerufen wird, übergibt die App den BluetoothSocket an einen separaten Thread übertragen, schließt den BluetoothServerSocket und bricht aus der Schleife ab.

Wenn accept() den BluetoothSocket zurückgibt, ist der Socket bereits verbunden. Daher sollten Sie nicht connect(), wie Sie es tun auf der Clientseite.

Die App-spezifische Methode manageMyConnectedSocket() dient dazu, Thread zum Übertragen von Daten, der im Thema Bluetooth wird übertragen Daten.

Normalerweise solltest du BluetoothServerSocket schließen, sobald du fertig bist auf eingehende Verbindungen warten. In diesem Beispiel wird close() sofort aufgerufen während BluetoothSocket übernommen wird. Sie können auch eine öffentliche in Ihrem Thread verwenden, mit der die private BluetoothSocket im Ereignis geschlossen werden kann dass der Server-Socket nicht mehr überwacht werden soll.

Als Kunde verbinden

Um eine Verbindung mit einem Remote-Gerät herzustellen, das diese akzeptiert über einen offenen Server-Socket ausführen, müssen Sie zuerst einen BluetoothDevice für das Remote-Gerät. Weitere Informationen zum Erstellen eines BluetoothDevice, siehe Bluetooth suchen Geräte. Du musst Verwenden Sie dann BluetoothDevice, um eine BluetoothSocket zu erhalten und den

Das grundlegende Verfahren sieht so aus:

  1. Rufen Sie mit der BluetoothDevice ein BluetoothSocket ab, indem Sie Folgendes aufrufen: createRfcommSocketToServiceRecord(UUID)

    Diese Methode initialisiert ein BluetoothSocket-Objekt, mit dem der Client Folgendes ausführen kann: eine Verbindung zu einem BluetoothDevice herstellen. Die hier übergebene UUID muss mit der verwendeten UUID übereinstimmen vom Servergerät erstellt, wenn er listenUsingRfcommWithServiceRecord(String, UUID) um die BluetoothServerSocket zu öffnen. Um eine übereinstimmende UUID zu verwenden, hartcodieren Sie die einen UUID-String in Ihre App ein und verweisen Sie dann sowohl vom Server und Clientcode.

  2. Starten Sie die Verbindung durch Aufrufen von connect(). Diese Methode ist ein Anruf wird blockiert.

    Nachdem ein Client diese Methode aufgerufen hat, führt das System eine SDP-Suche durch, das Remote-Gerät mit der entsprechenden UUID. Wenn die Suche erfolgreich ist und der Remote-Gerät akzeptiert die Verbindung und nutzt den RFCOMM-Kanal während der Verbindung und die Methode connect() wird zurückgegeben. Wenn die Verbindung oder wenn bei der Methode connect() eine Zeitüberschreitung auftritt (nach etwa 12 Sekunden), Die Methode löst einen IOException aus.

Diesen Aufruf solltest du immer ausführen, da connect() ein blockierender Aufruf ist Verbindungsverfahren in einem Thread, der von der Hauptaktivität (UI) getrennt ist Diskussions-Thread.

Beispiel

Im Folgenden finden Sie ein einfaches Beispiel für einen Client-Thread, der ein Bluetooth initiiert. Verbindung:

Kotlin

private inner class ConnectThread(device: BluetoothDevice) : Thread() {

   private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
       device.createRfcommSocketToServiceRecord(MY_UUID)
   }

   public override fun run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter?.cancelDiscovery()

       mmSocket?.let { socket ->
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           socket.connect()

           // The connection attempt succeeded. Perform work associated with
           // the connection in a separate thread.
           manageMyConnectedSocket(socket)
       }
   }

   // Closes the client socket and causes the thread to finish.
   fun cancel() {
       try {
           mmSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the client socket", e)
       }
   }
}

Java

private class ConnectThread extends Thread {
   private final BluetoothSocket mmSocket;
   private final BluetoothDevice mmDevice;

   public ConnectThread(BluetoothDevice device) {
       // Use a temporary object that is later assigned to mmSocket
       // because mmSocket is final.
       BluetoothSocket tmp = null;
       mmDevice = device;

       try {
           // Get a BluetoothSocket to connect with the given BluetoothDevice.
           // MY_UUID is the app's UUID string, also used in the server code.
           tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's create() method failed", e);
       }
       mmSocket = tmp;
   }

   public void run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter.cancelDiscovery();

       try {
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           mmSocket.connect();
       } catch (IOException connectException) {
           // Unable to connect; close the socket and return.
           try {
               mmSocket.close();
           } catch (IOException closeException) {
               Log.e(TAG, "Could not close the client socket", closeException);
           }
           return;
       }

       // The connection attempt succeeded. Perform work associated with
       // the connection in a separate thread.
       manageMyConnectedSocket(mmSocket);
   }

   // Closes the client socket and causes the thread to finish.
   public void cancel() {
       try {
           mmSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the client socket", e);
       }
   }
}

Beachten Sie, dass in diesem Snippet cancelDiscovery() vor der Verbindung aufgerufen wird. erfolgt. Rufen Sie cancelDiscovery() immer vor dem connect() auf. vor allem, weil cancelDiscovery() unabhängig davon funktioniert, Erkennung läuft gerade. Wenn Ihre App feststellen muss, Geräteerkennung läuft, können Sie dies mit isDiscovering()

Die App-spezifische Methode manageMyConnectedSocket() dient dazu, zum Übertragen von Daten, der im Abschnitt über Bluetooth-Daten übertragen.

Wenn du dein Gerät (BluetoothSocket) nicht mehr nutzt, ruf immer close() an. Vorgehensweise Schließt den angeschlossenen Socket sofort und gibt alle zugehörigen internen Ressourcen.