Android NDK开发详解连接性之传输蓝牙数据
成功连接到蓝牙设备后,每个设备都会有一个已连接的 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 将数据发送到主 activity。然后,该线程会等待从 InputStream 中读取更多字节。
如需发送传出数据,请从主 activity 调用线程的 write() 方法,并传入要发送的字节。此方法会调用 write(byte[]) 将数据发送到远程设备。如果在调用 write(byte[]) 时抛出 IOException,该线程会向主 activity 发送一个消息框,向用户说明设备无法将给定的字节发送到其他(连接的)设备。
通过线程的 cancel() 方法,您可以通过关闭 BluetoothSocket 随时终止连接。使用完蓝牙连接后,请始终调用此方法。
如需查看使用 Bluetooth API 的演示,请参阅 GitHub 上的蓝牙聊天示例应用。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2023-11-10。