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

来源:互联网 发布:南风知我意1七微百度云 编辑:程序博客网 时间:2024/06/04 19:21

场景:控制端——普通手机;被控制端——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方法,及时退出。

原创粉丝点击