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

场景:控制端——普通手机;被控制端——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();//在这里可以做一下记录
                            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方法,及时退出。

你可能感兴趣的:(原创)