Trasferire i dati Bluetooth

Dopo esserti connesso correttamente a un dispositivo Bluetooth, tutti tutti i dispositivi saranno connessi a BluetoothSocket. Ora puoi condividere informazioni tra dispositivi. Utilizzando BluetoothSocket, la procedura generale per trasferire i dati è la seguente:

  1. Prendi i dispositivi InputStream e OutputStream che gestiscono le trasmissioni tramite il socket utilizzando rispettivamente getInputStream() e getOutputStream().

  2. Leggi e scrivi dati nei flussi utilizzando read(byte[]) e write(byte[]).

Occorre ovviamente tenere conto dei dettagli dell'implementazione. In particolare, devi utilizzare un thread dedicato per leggere e scrivere dallo stream. Questo è importante perché entrambi i metodi read(byte[]) e write(byte[]) bloccano le chiamate. Il metodo read(byte[]) si blocca finché non ci è qualcosa da leggere dal flusso. Il metodo write(byte[]) di solito non si blocca, ma può bloccare il controllo del flusso se il dispositivo remoto non chiama read(byte[]) abbastanza rapidamente e, di conseguenza, i buffer intermedi si riempiono. Dovresti quindi dedicare il tuo ciclo principale nel thread alla lettura da InputStream. Puoi utilizzare un metodo pubblico separato nel thread per avviare le scritture in OutputStream.

Esempio

Di seguito è riportato un esempio di come è possibile trasferire dati tra due dispositivi connessi tramite Bluetooth:

Kotlin

private const val TAG = "MY_APP_DEBUG_TAG"

// Defines several constants used when transmitting messages between the
// service and the UI.
const val MESSAGE_READ: Int = 0
const val MESSAGE_WRITE: Int = 1
const val MESSAGE_TOAST: Int = 2
// ... (Add other message types here as needed.)

class MyBluetoothService(
       // handler that gets info from Bluetooth service
       private val handler: Handler) {

   private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {

       private val mmInStream: InputStream = mmSocket.inputStream
       private val mmOutStream: OutputStream = mmSocket.outputStream
       private val mmBuffer: ByteArray = ByteArray(1024) // mmBuffer store for the stream

       override fun run() {
           var numBytes: Int // bytes returned from read()

           // Keep listening to the InputStream until an exception occurs.
           while (true) {
               // Read from the InputStream.
               numBytes = try {
                   mmInStream.read(mmBuffer)
               } catch (e: IOException) {
                   Log.d(TAG, "Input stream was disconnected", e)
                   break
               }

               // Send the obtained bytes to the UI activity.
               val readMsg = handler.obtainMessage(
                       MESSAGE_READ, numBytes, -1,
                       mmBuffer)
               readMsg.sendToTarget()
           }
       }

       // Call this from the main activity to send data to the remote device.
       fun write(bytes: ByteArray) {
           try {
               mmOutStream.write(bytes)
           } catch (e: IOException) {
               Log.e(TAG, "Error occurred when sending data", e)

               // Send a failure message back to the activity.
               val writeErrorMsg = handler.obtainMessage(MESSAGE_TOAST)
               val bundle = Bundle().apply {
                   putString("toast", "Couldn't send data to the other device")
               }
               writeErrorMsg.data = bundle
               handler.sendMessage(writeErrorMsg)
               return
           }

           // Share the sent message with the UI activity.
           val writtenMsg = handler.obtainMessage(
                   MESSAGE_WRITE, -1, -1, mmBuffer)
           writtenMsg.sendToTarget()
       }

       // Call this method from the main activity to shut down the connection.
       fun cancel() {
           try {
               mmSocket.close()
           } catch (e: IOException) {
               Log.e(TAG, "Could not close the connect socket", e)
           }
       }
   }
}

Java

public class MyBluetoothService {
   private static final String TAG = "MY_APP_DEBUG_TAG";
   private Handler handler; // handler that gets info from Bluetooth service

   // Defines several constants used when transmitting messages between the
   // service and the UI.
   private interface MessageConstants {
       public static final int MESSAGE_READ = 0;
       public static final int MESSAGE_WRITE = 1;
       public static final int MESSAGE_TOAST = 2;

       // ... (Add other message types here as needed.)
   }

   private class ConnectedThread extends Thread {
       private final BluetoothSocket mmSocket;
       private final InputStream mmInStream;
       private final OutputStream mmOutStream;
       private byte[] mmBuffer; // mmBuffer store for the stream

       public ConnectedThread(BluetoothSocket socket) {
           mmSocket = socket;
           InputStream tmpIn = null;
           OutputStream tmpOut = null;

           // Get the input and output streams; using temp objects because
           // member streams are final.
           try {
               tmpIn = socket.getInputStream();
           } catch (IOException e) {
               Log.e(TAG, "Error occurred when creating input stream", e);
           }
           try {
               tmpOut = socket.getOutputStream();
           } catch (IOException e) {
               Log.e(TAG, "Error occurred when creating output stream", e);
           }

           mmInStream = tmpIn;
           mmOutStream = tmpOut;
       }

       public void run() {
           mmBuffer = new byte[1024];
           int numBytes; // bytes returned from read()

           // Keep listening to the InputStream until an exception occurs.
           while (true) {
               try {
                   // Read from the InputStream.
                   numBytes = mmInStream.read(mmBuffer);
                   // Send the obtained bytes to the UI activity.
                   Message readMsg = handler.obtainMessage(
                           MessageConstants.MESSAGE_READ, numBytes, -1,
                           mmBuffer);
                   readMsg.sendToTarget();
               } catch (IOException e) {
                   Log.d(TAG, "Input stream was disconnected", e);
                   break;
               }
           }
       }

       // Call this from the main activity to send data to the remote device.
       public void write(byte[] bytes) {
           try {
               mmOutStream.write(bytes);

               // Share the sent message with the UI activity.
               Message writtenMsg = handler.obtainMessage(
                       MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
               writtenMsg.sendToTarget();
           } catch (IOException e) {
               Log.e(TAG, "Error occurred when sending data", e);

               // Send a failure message back to the activity.
               Message writeErrorMsg =
                       handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
               Bundle bundle = new Bundle();
               bundle.putString("toast",
                       "Couldn't send data to the other device");
               writeErrorMsg.setData(bundle);
               handler.sendMessage(writeErrorMsg);
           }
       }

       // Call this method from the main activity to shut down the connection.
       public void cancel() {
           try {
               mmSocket.close();
           } catch (IOException e) {
               Log.e(TAG, "Could not close the connect socket", e);
           }
       }
   }
}

Dopo che il costruttore ha acquisito i flussi necessari, il thread attende che i dati passino attraverso InputStream. Quando read(byte[]) restituisce i dati dello stream, questi vengono inviati all'attività principale utilizzando un membro Handler della classe principale. Il thread attende quindi che vengano letti altri byte da InputStream.

Per inviare i dati in uscita, chiami il metodo write() del thread dall'attività principale e passi i byte da inviare. Questo metodo chiama write(byte[]) per inviare i dati al dispositivo remoto. Se viene generato un elemento IOException durante la chiamata a write(byte[]), il thread invia un avviso popup all'attività principale, spiegando all'utente che il dispositivo non è riuscito a inviare i byte all'altro dispositivo (connesso).

Il metodo cancel() del thread ti consente di terminare la connessione in qualsiasi momento chiudendo BluetoothSocket. Chiama sempre questo metodo quando smetti di utilizzare la connessione Bluetooth.

Per una dimostrazione di utilizzo delle API Bluetooth, vedi l'app di esempio per Bluetooth Chat su GitHub.