您的位置:首页 > 编程语言

蓝牙开发经验小结——蓝牙通讯

2017-12-05 15:43 417 查看
场景:控制端——普通手机;被控制端——XX设备(无屏幕、无法用户操作、有系统权限)

有关蓝牙通讯的文章,网上有很多,也有免费的开源代码下载,BluetoothChatService和BluetoothUtil是核心代码。(找不到源码的可以联系我的邮箱,欢迎交流)过多的实现细节,这里不会赘述,仅讲述一下我在开发过程中,遇到的问题及解决方案。

蓝牙通讯过程:蓝牙开启——找到设备——配对——建立蓝牙socket连接——通讯协议;然后,再搭建你的业务逻辑。该代码实现的蓝牙连接为1对1的模式,因此,当你发现控制端反复连不上XX设备时,请注意检查是否已有控制端连接到了你想控制的XX设备。

简单轻量地使用蓝牙socket收发,应该没有问题。我遇到的问题是数据包截断——当一次发送的数据长度超过1KB时,会自动截断。因此,当你需要一次发送较大数据量时,需要小心处理。

抛砖引玉,我的处理办法详述如下:

在发送端的处理:

private static String END_FLAG = "XVOaVb77FMYHyeTx";//采用16位随机数做结束标记符
public void sendMessage(String message) {
// Check that we're actually connected before trying anything
if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
//Toast.makeText(App.getInstance(), R.string.not_connected, Toast.LENGTH_SHORT).show();
logd("not connected yet.");
return;
}

//logd("send:"+message);
// Check that there's actually something to send
if (message.length() > 0) {
// Get the message bytes and tell the BluetoothChatService to write
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(message).append(END_FLAG);//用结束符来标记,防止数据过长,导致控制端因数据截断而无法识别的问题
byte[] send = stringBuilder.toString().getBytes();
mChatService.write(send);
}
}


接收端的处理:

private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;

public ConnectedThread(BluetoothSocket socket, String socketType) {
logd("create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;

// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
logd("temp sockets not created"+e);
}

mmInStream = tmpIn;
mmOutStream = tmpOut;
}

public void run() {
logd("BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
StringBuilder stringBuilder = new StringBuilder();

// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
stringBuilder.append( new String(buffer, 0, bytes));
//logd("length="+stringBuilder.length());

if (stringBuilder.toString().contains(END_FLAG) && stringBuilder.toString().endsWith(END_FLAG)) {
String[] msgs = stringBuilder.toString().split(END_FLAG);
for (String s : msgs) {
mHandler.obtainMessage(BluetoothUtil.MESSAGE_READ, bytes, -1, s).sendToTarget();
}
stringBuilder = new StringBuilder();
}
} catch (IOException e) {
logd("disconnected"+e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
}
}
}


该处理办法经过实际验证是可行的。

另外,使用指定mac的连接方式,作为client端通常是已知对端的蓝牙mac的,而若server端要知道client的蓝牙mac,可以在如下节点获取并做一下保存(AcceptThread中的run方法):

// Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
logd("Socket Type: " + mSocketType + "accept() failed"+e);
break;
}

// If a connection was accepted
if (socket != null) {
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
clientDevie = socket.getRemoteDevice();//在这里可以做一下
4000
记录
logd("client device address :" + clientDevie.getAddress());
connected(socket, clientDevie, mSocketType);
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
logd("Could not close unwanted socket"+e);
}
break;
}
}
}
}


这份代码存在一些crash的bug,例如BluetoothChatService没有退出就直接关闭掉蓝牙会出现crash,因此最好注册蓝牙状态的广播监听,若收到“BluetoothAdapter.STATE_TURNING_OFF”的广播,就直接调用BluetoothChatService的stop方法,及时退出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息