블루투스 기기에 연결하면 각 기기에는 BluetoothSocket
이 연결됩니다. 이제 기기 간에 정보를 공유할 수 있습니다. BluetoothSocket
을 사용하여 데이터를 전송하는 일반적인 절차는 다음과 같습니다.
getInputStream()
및getOutputStream()
을 각각 사용하여 소켓을 통해 전송을 처리하는InputStream
및OutputStream
를 가져옵니다.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의 블루투스 채팅 샘플 앱을 참고하세요.