Connetti dispositivi Bluetooth

Per creare una connessione tra due dispositivi, devi implementare entrambi meccanismi lato server e lato client perché un dispositivo deve aprire un server e l'altro deve avviare la connessione utilizzando il protocollo Indirizzo MAC. Sia il dispositivo server che il dispositivo client ottengono i contenuti necessari BluetoothSocket in diverse in molti modi diversi. Il server riceve informazioni sul socket quando viene rilevata una connessione in entrata accettato. Il client fornisce informazioni sul socket quando apre un canale RFCOMM al server.

Il server e il client sono considerati connessi l'uno all'altro quando BluetoothSocket collegato sullo stesso canale RFCOMM. A questo punto, ogni dispositivo può ricevere flussi di input e di output e il trasferimento dei dati può iniziare, viene discussa nella sezione relativa al trasferimento Bluetooth i tuoi dati. Questa sezione descrive come avviare la connessione tra due dispositivi.

Assicurati di disporre dell'appropriata Autorizzazioni Bluetooth e configurare l'app per il Bluetooth prima del giorno tentativo di trovare dispositivi Bluetooth.

Tecniche di connessione

Una tecnica di implementazione è preparare automaticamente ogni dispositivo come server in modo che ogni dispositivo abbia un socket del server aperto e in ascolto delle connessioni. Nella In questo caso, uno dei due dispositivi può avviare una connessione tra loro e diventare di alto profilo. In alternativa, un dispositivo può ospitare esplicitamente la connessione e aprire dal socket del server on demand e l'altro dispositivo avvia la connessione.

e
Figura 1. La finestra di dialogo per l'accoppiamento Bluetooth.

Connettiti come server

Quando vuoi connettere due dispositivi, uno deve fungere da server tenendo un apri BluetoothServerSocket Lo scopo del socket del server è ascoltare le richieste di connessione in entrata e fornire un BluetoothSocket collegato dopo l'accettazione della richiesta. Quando BluetoothSocket viene acquisito da BluetoothServerSocket, BluetoothServerSocket può e deve essere eliminato, a meno che tu non voglia sul dispositivo per accettare altre connessioni.

Per configurare un socket del server e accettare una connessione, completa la procedura seguente sequenza di passaggi:

  1. Ricevi BluetoothServerSocket chiamando listenUsingRfcommWithServiceRecord(String, UUID)

    La stringa è un nome identificabile del servizio, che il sistema scrive automaticamente su una nuova voce del database Service Discovery Protocol (SDP) sul dispositivo. Il nome è arbitrario e può essere semplicemente il nome della tua app. La voce SDP include anche l'Universally Unique Identifier (UUID) e costituisce la base del contratto di connessione con il dispositivo client. Questo è che quando il client tenta di connettersi a questo dispositivo, utilizza un UUID che identifichi in modo univoco il servizio a cui vuole connettersi. Questi Gli UUID devono corrispondere affinché la connessione venga accettata.

    Un UUID è un formato standardizzato a 128 bit per un ID stringa utilizzato per identificare le informazioni. L'UUID viene utilizzato per identificare le informazioni che devono essere univoco all'interno di un sistema o di una rete perché è probabile che un UUID sia ripetuto è pari a zero. Viene generato in modo indipendente, senza l'utilizzo di un'autorità centralizzata. In questo caso, viene utilizzato per identificare in modo univoco servizio Bluetooth dell'app. Per ottenere un UUID da usare con la tua app, puoi usarne uno dei molti UUID sul web, quindi inizializza un'immagine UUID con fromString(String).

  2. Inizia ad ascoltare le richieste di connessione chiamando accept()

    Questa è una chiamata di blocco. Viene restituito quando una connessione è stata accettati o si è verificata un'eccezione. La connessione viene accettata solo quando il dispositivo remoto ha inviato una richiesta di connessione contenente un UUID che corrisponde quello registrato con il socket del server di ascolto. Una volta completata l'operazione, accept() restituisce un BluetoothSocket connesso.

  3. A meno che tu non voglia accettare altri contatti, chiama close()

    Questa chiamata al metodo rilascia il socket del server e tutte le sue risorse, ma non chiude il dispositivo BluetoothSocket connesso restituito da accept(). A differenza di TCP/IP, RFCOMM consente un solo client connesso per canale alla volta, quindi nella maggior parte dei casi ha senso chiamare close() BluetoothServerSocket subito dopo l'accettazione di un socket connesso.

Poiché la chiamata accept() è di blocco, non eseguirla nell'istanza thread della UI delle attività. L'esecuzione in un altro thread assicura che la tua app possa rispondere comunque ad altre interazioni degli utenti. Di solito ha senso svolgere tutte le attività che coinvolge BluetoothServerSocket o BluetoothSocket in un nuovo thread gestiti dalla tua app. Per interrompere una chiamata bloccata, ad esempio accept(), chiama close() in BluetoothServerSocket o BluetoothSocket da un altro thread. Nota che tutti i metodi su BluetoothServerSocket o BluetoothSocket siano sicura per i thread.

Esempio

Di seguito è riportato un thread semplificato per il componente server che accetta connessioni in entrata:

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 questo esempio, occorre una sola connessione in entrata, quindi non appena connessione è accettata e il BluetoothSocket viene acquisito, l'app trasmette il acquisito BluetoothSocket in un thread separato, chiude BluetoothServerSocket ed esce dal ciclo.

Tieni presente che quando accept() restituisce BluetoothSocket, il socket è già connesso. Pertanto, non devi chiamare connect(), mentre procedi dal lato client.

Il metodo manageMyConnectedSocket() specifico dell'app è progettato per avviare il per il trasferimento dei dati, di cui abbiamo parlato nell'argomento trasferimento Bluetooth di archiviazione dati.

In genere, devi chiudere BluetoothServerSocket appena hai finito per ascoltare le connessioni in arrivo. In questo esempio, close() viene chiamato con l'acquisizione di BluetoothSocket. Puoi anche fornire un'interfaccia nel thread che può chiudere l'elemento BluetoothSocket privato nell'evento che devi interrompere l'ascolto sul socket del server.

Connettiti come cliente

Per avviare una connessione con un dispositivo remoto che accetta su un socket server aperto, devi prima ottenere un BluetoothDevice che rappresenta il dispositivo remoto. Per scoprire come creare un BluetoothDevice, leggi l'articolo Trovare il Bluetooth dispositivi mobili. Devi utilizza BluetoothDevice per acquisire un BluetoothSocket e avviare la connessione.

La procedura di base è la seguente:

  1. Utilizzando l'BluetoothDevice, ricevi BluetoothSocket chiamando createRfcommSocketToServiceRecord(UUID)

    Questo metodo inizializza un oggetto BluetoothSocket che consente al client di connettersi a un BluetoothDevice. L'UUID trasferito qui deve corrispondere all'UUID utilizzato dal dispositivo server quando ha chiamato listenUsingRfcommWithServiceRecord(String, UUID) per aprire il relativo BluetoothServerSocket. Per utilizzare un UUID corrispondente, esegui l'hard-coding del la stringa UUID nell'app e farvi riferimento sia dal server e il codice client.

  2. Avvia la connessione chiamando il numero connect(). Tieni presente che questo metodo è un bloccare la chiamata.

    Dopo che un client chiama questo metodo, il sistema esegue una ricerca SDP per trovare il dispositivo remoto con l'UUID corrispondente. Se la ricerca ha esito positivo e dispositivo remoto accetta la connessione, condivide il canale RFCOMM per l'utilizzo durante la connessione e restituisce il metodo connect(). Se la connessione non riesce o se il metodo connect() si verifica in timeout (dopo circa 12 secondi), il metodo genera un IOException.

Poiché connect() è una chiamata di blocco, dovresti sempre eseguirla in un thread separato dall'attività principale (UI) .

Esempio

Di seguito è riportato un esempio base di thread client che avvia una connessione Bluetooth connessione:

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

Tieni presente che in questo snippet, cancelDiscovery() viene chiamato prima della connessione che si verifica. Devi sempre chiamare cancelDiscovery() prima del giorno connect(), soprattutto perché cancelDiscovery() va a buon fine a prescindere dal fatto che il dispositivo è attualmente in corso. Se la tua app ha bisogno di determinare è in corso il rilevamento del dispositivo. Puoi controllare isDiscovering()

Il metodo manageMyConnectedSocket() specifico dell'app è progettato per avviare il per il trasferimento dei dati, illustrato nella sezione trasferire dati Bluetooth.

Quando hai finito di usare il tuo BluetoothSocket, chiama sempre close(). In questo modo chiude immediatamente il socket collegato e rilascia tutti i componenti Google Cloud.