블루투스 데이터 전송

블루투스 기기에 연결하면 각 기기에는 BluetoothSocket이 연결됩니다. 이제 기기 간에 정보를 공유할 수 있습니다. BluetoothSocket을 사용하여 데이터를 전송하는 일반적인 절차는 다음과 같습니다.

  1. getInputStream()getOutputStream()을 각각 사용하여 소켓을 통해 전송을 처리하는 InputStreamOutputStream를 가져옵니다.

  2. read(byte[])write(byte[])를 사용하여 스트림에 데이터를 읽고 씁니다.

물론 세부적인 구현을 고려해야 합니다. 특히 스트림에서 읽고 쓰려면 전용 스레드를 사용해야 합니다. 이는 read(byte[]) 메서드와 write(byte[]) 메서드 모두 차단 호출을 사용하므로 중요합니다. read(byte[]) 메서드는 스트림에서 읽을 항목이 있을 때까지 차단합니다. write(byte[]) 메서드는 일반적으로 차단하지 않지만 원격 기기가 read(byte[])를 빠르게 호출하지 않아 중간 버퍼가 가득 차면 흐름 제어를 위해 차단할 수 있습니다. 따라서 InputStream에서 읽는 전용 스레드의 기본 루프를 전용해야 합니다. 스레드에서 별도의 공개 메서드를 사용하여 OutputStream에 쓰기를 시작할 수 있습니다.

다음은 블루투스를 통해 연결된 두 기기 간에 데이터를 전송하는 방법을 보여주는 예입니다.

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

생성자가 필요한 스트림을 획득하면 스레드는 데이터가 InputStream를 통해 올 때까지 기다립니다. read(byte[])가 스트림의 데이터를 반환하면 데이터는 상위 클래스의 멤버 Handler를 사용하여 기본 활동으로 전송됩니다. 그런 다음 스레드는 InputStream에서 추가 바이트를 읽을 때까지 기다립니다.

발신 데이터를 전송하려면 기본 활동에서 스레드의 write() 메서드를 호출하고 전송할 바이트를 전달합니다. 이 메서드는 write(byte[])를 호출하여 데이터를 원격 기기로 전송합니다. write(byte[])를 호출할 때 IOException이 발생하면 스레드는 기본 활동에 토스트 메시지를 보내 기기에서 지정된 바이트를 다른 연결된 기기로 보낼 수 없다고 사용자에게 설명합니다.

스레드의 cancel() 메서드를 사용하면 언제든지 BluetoothSocket를 닫아 연결을 종료할 수 있습니다. 블루투스 연결 사용을 마치면 항상 이 메서드를 호출합니다.

블루투스 API를 사용하는 방법에 관한 데모는 GitHub의 블루투스 채팅 샘플 앱을 참고하세요.