Connecter appareils Bluetooth

Pour créer une connexion entre deux appareils, vous devez implémenter à la fois les des mécanismes côté serveur et côté client, car un appareil doit ouvrir un serveur socket, et l'autre doit initier la connexion à l'aide de l'adresse IP adresse MAC. Le serveur et l'appareil client obtiennent chacun l'autorisation BluetoothSocket dans différentes de différentes manières. Le serveur reçoit des informations de socket lorsqu'une connexion entrante est accepté. Le client fournit des informations de socket lorsqu'il ouvre un canal RFCOMM sur le serveur.

Le serveur et le client sont considérés comme connectés l'un à l'autre lorsqu'ils ont chacun un BluetoothSocket connecté sur le même canal RFCOMM. À ce stade, chaque l'appareil peut obtenir des flux d'entrée et de sortie, et le transfert de données peut commencer, ce qui est abordée dans la section concernant le transfert Bluetooth données. Cette section explique comment établir la connexion entre deux appareils.

Assurez-vous de disposer des autorisations Autorisations Bluetooth configurer votre application pour le Bluetooth avant lorsque vous essayez de trouver des appareils Bluetooth.

Techniques de connexion

Une technique d'implémentation consiste à préparer automatiquement chaque appareil en tant que serveur de sorte que chaque appareil ait un socket de serveur ouvert et écoutant les connexions. Dans dans ce cas, l'un ou l'autre appareil peut établir une connexion avec l'autre et devenir le client. Un appareil peut aussi héberger explicitement la connexion et ouvrir un socket serveur à la demande, et l'autre appareil lance la connexion.


Figure 1 : Boîte de dialogue d'association Bluetooth.

Se connecter en tant que serveur

Lorsque vous souhaitez connecter deux appareils, l’un doit agir en tant que serveur en tenant un ouvert BluetoothServerSocket L'objectif du socket serveur est d'écouter les requêtes de connexion entrantes et fournir un BluetoothSocket associé une fois la requête acceptée. Lorsque BluetoothSocket est acquise à partir de BluetoothServerSocket, le BluetoothServerSocket peut et doit être supprimé, sauf si vous le souhaitez l'appareil pour accepter plus de connexions.

Pour configurer un socket serveur et accepter une connexion, procédez comme suit : séquence d'étapes:

  1. Obtenez un BluetoothServerSocket en appelant listenUsingRfcommWithServiceRecord(String, UUID)

    Il s'agit d'un nom identifiable pour votre service, que le système écrit automatiquement dans une nouvelle entrée de base de données SDP (Service Discovery Protocol) sur l'appareil. Le nom est arbitraire. Il peut simplement correspondre au nom de votre application. L'identifiant unique universel (UUID) est également inclus dans l'entrée SDP. et constitue la base de l'accord de connexion avec l'appareil client. Cela lorsque le client tente de se connecter à ce périphérique, il conserve un UUID qui identifie de manière unique le service auquel il veut se connecter. Ces Les UUID doivent correspondre pour que la connexion soit acceptée.

    Un UUID est un format normalisé de 128 bits pour un identifiant de chaîne utilisé de manière unique des informations d'identification. Un UUID permet d'identifier les informations qui doivent être est unique au sein d'un système ou d'un réseau, car la probabilité qu'un UUID soit répété est effectivement zéro. Elles sont générées de façon indépendante, d'une autorité centralisée. Dans ce cas, il permet d'identifier votre service Bluetooth de l'application. Pour obtenir un UUID à utiliser avec votre application, vous pouvez en utiliser un. parmi les nombreux UUID sur le Web, puis initialisez un UUID avec fromString(String)

  2. Commencez à écouter les demandes de connexion en appelant accept()

    Il s'agit d'un appel bloquant. Elle est renvoyée lorsqu'une connexion a été acceptée ou une exception s'est produite. Une connexion n'est acceptée que lorsqu'un L'appareil distant a envoyé une demande de connexion contenant un UUID qui correspond celui enregistré avec ce socket de serveur d'écoute. En cas de succès, accept() renvoie un BluetoothSocket connecté.

  3. À moins que vous ne souhaitiez accepter des connexions supplémentaires, appelez close()

    Cet appel de méthode libère le socket serveur et toutes ses ressources, mais ne ferme pas le BluetoothSocket connecté qui a été renvoyé par accept() Contrairement à TCP/IP, RFCOMM n'autorise qu'un seul client connecté par canal à la fois. Dans la plupart des cas, il est donc judicieux d'appeler close() sur le BluetoothServerSocket immédiatement après avoir accepté un socket connecté.

Comme l'appel accept() est un appel bloquant, ne l'exécutez pas dans l'instance main thread UI de l'activité. Si vous l'exécutez dans un autre thread, votre application peut tout en continuant à réagir aux autres interactions des utilisateurs. Il est généralement judicieux de faire tout le travail qui implique un BluetoothServerSocket ou un BluetoothSocket dans un nouveau thread gérés par votre application. Pour annuler un appel bloqué tel que accept(), appelez close() sur le BluetoothServerSocket ou BluetoothSocket d'un autre fil de discussion. Remarque que toutes les méthodes d'un BluetoothServerSocket ou d'un BluetoothSocket sont thread-safe.

Exemple

Voici un thread simplifié pour le composant de serveur qui accepte connexions entrantes:

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

Dans cet exemple, une seule connexion entrante est souhaitée, donc dès qu'un la connexion est acceptée et que le BluetoothSocket est acquis, l'application transmet acquis BluetoothSocket dans un thread distinct, ferme BluetoothServerSocket, puis sort de la boucle.

Notez que lorsque accept() renvoie BluetoothSocket, le socket est déjà connectés. Vous ne devez donc pas appeler connect(), comme vous le faites côté client.

La méthode manageMyConnectedSocket() propre à l'application est conçue pour lancer pour transférer des données, qui est abordé dans l'article sur transfert Bluetooth données.

En général, vous devriez fermer votre BluetoothServerSocket dès que vous avez terminé à l'écoute des connexions entrantes. Dans cet exemple, close() est appelé dès que lorsque BluetoothSocket est acquis. Vous pouvez également mettre à disposition dans votre fil de discussion qui peut fermer le BluetoothSocket privé dans l'événement que vous devez arrêter d'écouter sur ce socket serveur.

Se connecter en tant que client

Afin d'établir une connexion avec un appareil distant qui accepte sur un socket de serveur ouvert, vous devez d'abord obtenir un BluetoothDevice qui représente l'appareil distant. Pour apprendre à créer un BluetoothDevice, consultez Trouver le Bluetooth appareils. Vous devez Utilisez ensuite BluetoothDevice pour acquérir un BluetoothSocket et lancer le .

La procédure de base est la suivante:

  1. À l'aide de BluetoothDevice, obtenez un BluetoothSocket en appelant createRfcommSocketToServiceRecord(UUID)

    Cette méthode initialise un objet BluetoothSocket qui permet au client de connectez-vous à un BluetoothDevice. L'UUID transmis ici doit correspondre à l'UUID utilisé. par le périphérique serveur lorsqu'il a appelé listenUsingRfcommWithServiceRecord(String, UUID) pour ouvrir son BluetoothServerSocket. Pour utiliser un UUID correspondant, codez en dur le la chaîne UUID dans votre application, puis la référencer à partir du serveur et le code client.

  2. Établissez la connexion en appelant connect(). Notez que cette méthode bloquant l'appel.

    Une fois qu'un client a appelé cette méthode, le système effectue une recherche SDP pour trouver l'appareil distant avec l'UUID correspondant. Si la recherche réussit et que l'appareil distant accepte la connexion, il partage le canal RFCOMM pour utiliser pendant la connexion, et la méthode connect() renvoie un résultat. Si la connexion échoue, ou si la méthode connect() expire (au bout de 12 secondes environ), alors la méthode génère une exception IOException.

Comme connect() est un appel bloquant, vous devez toujours effectuer cette procédure de connexion dans un thread distinct de l'activité principale (UI) thread.

Exemple

Voici un exemple de base de thread client qui lance une connexion Bluetooth connexion:

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

Notez que dans cet extrait, cancelDiscovery() est appelé avant la connexion d'une tentative d'accès. Vous devez toujours appeler cancelDiscovery() avant connect(), d'autant plus que cancelDiscovery() réussit, que l'appareil découverte est en cours. Si votre application doit déterminer si la détection d'appareils est en cours, vous pouvez vérifier isDiscovering()

La méthode manageMyConnectedSocket() propre à l'application est conçue pour lancer pour transférer des données, abordé dans la section transférer des données Bluetooth.

Lorsque vous avez terminé d'utiliser votre BluetoothSocket, appelez toujours close(). Procéder à cette opération ferme immédiatement le socket connecté et libère toutes les connexions ressources.