Conecta dispositivos Bluetooth

Para crear una conexión entre dos dispositivos, debes implementar el Mecanismos del servidor y del cliente, ya que un dispositivo debe abrir un servidor y el otro debe iniciar la conexión usando la interfaz del dispositivo dirección MAC. El dispositivo del servidor y el dispositivo cliente obtienen la BluetoothSocket en diferentes maneras. El servidor recibe información del socket cuando se inicia una conexión aceptada. El cliente proporciona información del socket cuando abre un canal RFCOMM al servidor.

El servidor y el cliente se consideran conectados entre sí cuando tienen un BluetoothSocket conectado en el mismo canal RFCOMM En este punto, cada puede obtener flujos de entrada y salida, y comenzar la transferencia de datos, que se analiza en la sección sobre cómo transferir dispositivos los datos en la nube. Esta sección describe cómo iniciar la conexión entre dos dispositivos.

Asegúrate de tener la información Permisos de Bluetooth y configura tu app para usar Bluetooth antes intentando encontrar dispositivos Bluetooth.

Técnicas de conexión

Una técnica de implementación consiste en preparar automáticamente cada dispositivo como un servidor de manera que cada dispositivo tenga un socket de servidor abierto y reciba conexiones. En en este caso, cualquiera de los dispositivos puede iniciar una conexión y convertirse en cliente. De manera alternativa, un dispositivo puede alojar explícitamente la conexión y abrir una un socket de servidor bajo demanda, y el otro dispositivo iniciará la conexión.


Figura 1: Diálogo de vinculación por Bluetooth

Conectarse como servidor

Cuando quieras conectar dos dispositivos, uno debe funcionar como servidor manteniendo un abierta BluetoothServerSocket El propósito del socket de servidor es escuchar las solicitudes de conexión entrantes. y proporciona un BluetoothSocket conectado después de que se acepte una solicitud. Cuando BluetoothSocket se adquiere de BluetoothServerSocket, el BluetoothServerSocket puede (y debe) descartarse, a menos que quieras que el dispositivo acepte más conexiones.

Para configurar un socket de servidor y aceptar una conexión, completa los siguientes pasos: secuencia de pasos:

  1. Obtén un BluetoothServerSocket llamando listenUsingRfcommWithServiceRecord(String, UUID)

    La cadena es un nombre identificable de tu servicio, que el sistema escribe automáticamente en una nueva entrada de base de datos del protocolo de detección de servicios (SDP) en el dispositivo. El nombre es arbitrario y puede ser simplemente el de tu app. El identificador único universal (UUID) también se incluye en la entrada del SDP y constituye la base del acuerdo de conexión con el dispositivo del cliente. Que es que, cuando el cliente intenta conectarse con este dispositivo, lleva un UUID que identifique de manera inequívoca el servicio con el que desea conectarse. Estos Los UUID deben coincidir a fin de que se acepte la conexión.

    Un UUID es un formato estandarizado de 128 bits para un ID de cadena que se usa para identificar información. Un UUID se usa para identificar la información que se debe únicos en un sistema o red porque la probabilidad de que un UUID sea repetido es efectivamente cero. Se genera de forma independiente, sin necesidad de de una autoridad centralizada. En este caso, se usa para identificar de forma única tu servicio Bluetooth de la app. Para obtener un UUID que pueda usarse con la app, puedes usar uno de muchos modelos aleatorios UUID en la Web y, luego, inicializa un UUID con fromString(String)

  2. Llama para comenzar a escuchar solicitudes de conexión accept()

    Esta es una llamada de bloqueo. Se muestra cuando se establece una conexión o se produjo una excepción. Una conexión se acepta solo cuando una el dispositivo remoto envió una solicitud de conexión que contiene un UUID que coincide el que está registrado con este socket de servidor de escucha. Si tiene éxito, accept() muestra un BluetoothSocket conectado.

  3. A menos que quieras aceptar conexiones adicionales, llama close()

    Este método libera el socket de servidor y todos sus recursos, pero no cierra el BluetoothSocket conectado que devolvió accept() A diferencia de TCP/IP, RFCOMM permite solo un cliente conectado por canal a la vez, así que, en la mayoría de los casos, tiene sentido llamar a close() en el BluetoothServerSocket inmediatamente después de aceptar un socket conectado.

Dado que la llamada a accept() es de bloqueo, no la ejecutes en el de la IU de la actividad. Ejecutarla en otro subproceso garantiza que la app pueda responder a otras interacciones del usuario. Por lo general, tiene sentido hacer todo el trabajo que implique un BluetoothServerSocket o BluetoothSocket en un subproceso nuevo administrados por tu app. Para anular una llamada bloqueada, como accept(), llama a close() en BluetoothServerSocket o BluetoothSocket de otro subproceso. Nota que todos los métodos en un BluetoothServerSocket o BluetoothSocket están de forma segura para subprocesos.

Ejemplo

El siguiente es un subproceso simplificado para el componente de servidor que acepta conexiones 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);
       }
   }
}

En este ejemplo, solo se desea una conexión entrante, por lo que como se acepta la conexión y se adquiere el BluetoothSocket, la app pasa el adquirido BluetoothSocket en un subproceso independiente, se cierra BluetoothServerSocket y sale del bucle.

Ten en cuenta que cuando accept() devuelve BluetoothSocket, el socket ya está conectado. Por lo tanto, no debes llamar connect(), como tú del lado del cliente.

El método manageMyConnectedSocket() específico de la app está diseñado para iniciar la conversación para transferir datos, que se analiza en el tema sobre transfiriendo Bluetooth datos.

Por lo general, debes cerrar tu BluetoothServerSocket en cuanto termines. a la escucha de conexiones entrantes. En este ejemplo, se llama a close() en cuanto a medida que se adquiere BluetoothSocket. También puedes proporcionar una de tu subproceso que pueda cerrar el BluetoothSocket privado en el evento que debes dejar de escuchar en ese socket de servidor.

Conectarse como cliente

Para iniciar una conexión con un dispositivo remoto que acepte conexiones en un socket de servidor abierto, primero debes obtener un BluetoothDevice que representa el dispositivo remoto. Para aprender a crear un BluetoothDevice, consulta Buscar Bluetooth dispositivos. Debes Luego, usa el BluetoothDevice para adquirir un BluetoothSocket e iniciar la conexión.

El procedimiento básico es el siguiente:

  1. Con BluetoothDevice, obtén un BluetoothSocket llamando createRfcommSocketToServiceRecord(UUID)

    Este método inicializa un objeto BluetoothSocket que le permite al cliente hacer lo siguiente: conectarte a BluetoothDevice. El UUID que se pasa aquí debe coincidir con el UUID usado por el dispositivo del servidor cuando llamó listenUsingRfcommWithServiceRecord(String, UUID) para abrir su BluetoothServerSocket. Para usar un UUID que coincida, codifica el de UUID a tu app y, luego, hacer referencia a ella desde el servidor y el código del cliente.

  2. Llama a connect() para iniciar la conexión. Ten en cuenta que este método es un llamada de bloqueo.

    Después de que un cliente llama a este método, el sistema realiza una búsqueda de SDP para encontrar el dispositivo remoto con el UUID correspondiente. Si la búsqueda se realiza correctamente y El dispositivo remoto acepta la conexión y comparte el canal RFCOMM para usar durante la conexión, y se devuelve el método connect(). Si la conexión falla o si se agota el tiempo de espera del método connect() (después de unos 12 segundos), el método arroja una IOException.

Como connect() es una llamada de bloqueo, siempre debes realizar esto. procedimiento de conexión en un subproceso independiente de la actividad principal (IU) conversación.

Ejemplo

El siguiente es un ejemplo básico de un subproceso de cliente que inicia una conexión Bluetooth conexión:

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

Ten en cuenta que, en este fragmento, se llama a cancelDiscovery() antes de la conexión se produce un primer intento. Siempre debes llamar a cancelDiscovery() antes de connect(), en especial porque cancelDiscovery() funciona correctamente sin importar si el dispositivo el descubrimiento está en curso. Si tu app necesita determinar si la detección de dispositivos está en curso, puedes comprobarlo con isDiscovering()

El método manageMyConnectedSocket() específico de la app está diseñado para iniciar la subproceso para transferir datos, que se analiza en la sección sobre transferencia de datos de Bluetooth.

Cuando termines de usar BluetoothSocket, siempre llama a close(). Hacerlo cierra de inmediato el socket conectado y libera todos los procesos de Google Cloud.