时间:2020-10-10 11:46:26 | 栏目:Android代码 | 点击:次
蓝牙,时下最流行的智能设备传输数据的方式之一,通过手机app和智能设备进行连接,获取设备上的测量数据,我们生活中随处可见的比如蓝牙智能手环,蓝牙电子秤,蓝牙心电测量设备等等。
本篇我将紧接着上篇结尾所写,一起来看下手机之间如何通过蓝牙实现文字聊天。
先贴出上篇的一些demo;
当点击图上的两个列表中的任何一个列表,执行如下代码:
mBtAdapter.cancelDiscovery(); String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); setResult(Activity.RESULT_OK, intent); finish();
此蓝牙聊天工具最后实现的效果是这样的:
将回到聊天主界面:
public void onActivityResult(int requestCode, int resultCode, Intent data) { LogUtils.getInstance().e(getClass(), "onActivityResult " + resultCode); switch (requestCode) { case REQUEST_CONNECT_DEVICE: // 当DeviceListActivity返回与设备连接的消息 if (resultCode == Activity.RESULT_OK) { // 连接设备的MAC地址 String address = data.getExtras().getString( DeviceListActivity.EXTRA_DEVICE_ADDRESS); // 得到蓝牙对象 BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); // 开始连接设备 mChatService.connect(device); } break; case REQUEST_ENABLE_BT: // 判断蓝牙是否启用 if (resultCode == Activity.RESULT_OK) { // 建立连接 setupChat(); } else { LogUtils.getInstance().e(getClass(), "蓝牙未启用"); Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show(); finish(); } } }
在此,我将重点介绍下BluetoothChatService类中的连接流程;
因为蓝牙聊天是两个手机之间进行通讯,所以他们互为主机和从机,主要思路以及步骤如下:
1.开一个线程获取socket去连接蓝牙;
2.开一个线程获监听蓝牙传入的连接,如果连接被接受的话,再开启第三个线程去处理所有传入和传出的数据;
public synchronized void connect(BluetoothDevice device) { if (mState == STATE_CONNECTING) { if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } } if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } mConnectThread = new ConnectThread(device); mConnectThread.start(); setState(STATE_CONNECTING); }
开线程去连接
/** * @description:蓝牙连接线程 * @author:zzq * @time: 2016-8-6 下午1:18:41 */ private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; try { tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { LogUtils.getInstance().e(getClass(), "socket获取失败:" + e); } mmSocket = tmp; } public void run() { LogUtils.getInstance().e(getClass(), "开始mConnectThread"); setName("ConnectThread"); // mAdapter.cancelDiscovery(); try { mmSocket.connect(); } catch (IOException e) { // 连接失败,更新ui connectionFailed(); try { mmSocket.close(); } catch (IOException e2) { LogUtils.getInstance().e(getClass(), "关闭连接失败" + e2); } // 开启聊天接收线程 startChat(); return; } synchronized (BluetoothChatService.this) { mConnectThread = null; } connected(mmSocket, mmDevice); } public void cancel() { try { mmSocket.close(); } catch (IOException e) { LogUtils.getInstance().e(getClass(), "关闭连接失败" + e); } } }
/** * 监听传入的连接 */ private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket; public AcceptThread() { BluetoothServerSocket tmp = null; try { tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { LogUtils.getInstance().e(getClass(), "--获取socket失败:" + e); } mmServerSocket = tmp; } public void run() { setName("AcceptThread"); BluetoothSocket socket = null; while (mState != STATE_CONNECTED) { LogUtils.getInstance().e(getClass(), "----accept-循环执行中-"); try { socket = mmServerSocket.accept(); } catch (IOException e) { LogUtils.getInstance().e(getClass(), "accept() 失败" + e); break; } // 如果连接被接受 if (socket != null) { synchronized (BluetoothChatService.this) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: // 开始连接线程 connected(socket, socket.getRemoteDevice()); break; case STATE_NONE: case STATE_CONNECTED: // 没有准备好或已经连接 try { socket.close(); } catch (IOException e) { LogUtils.getInstance().e(getClass(),"不能关闭这些连接" + e); } break; } } } } LogUtils.getInstance().e(getClass(), "结束mAcceptThread"); } public void cancel() { LogUtils.getInstance().e(getClass(), "取消 " + this); try { mmServerSocket.close(); } catch (IOException e) { LogUtils.getInstance().e(getClass(), "关闭失败" + e); } } } /** * 连接成功后的线程 处理所有传入和传出的传输 */ private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket) { mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // 得到BluetoothSocket输入和输出流 try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { LogUtils.getInstance().e(getClass(),"temp sockets not created" + e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { int bytes; String str1 = ""; // 循环监听消息 while (true) { try { byte[] buffer = new byte[256]; bytes = mmInStream.read(buffer); String readStr = new String(buffer, 0, bytes);// 字节数组直接转换成字符串 String str = bytes2HexString(buffer).replaceAll("00", "").trim(); if (bytes > 0) {// 将读取到的消息发到主线程 mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_READ, bytes, -1,buffer).sendToTarget(); } else { LogUtils.getInstance().e(getClass(),"disconnected"); connectionLost(); if (mState != STATE_NONE) { LogUtils.getInstance().e(getClass(), "disconnected"); startChat(); } break; } } catch (IOException e) { LogUtils.getInstance().e(getClass(), "disconnected" + e); connectionLost(); if (mState != STATE_NONE) { // 在重新启动监听模式启动该服务 startChat(); } break; } } } /** * 写入OutStream连接 * * @param buffer * 要写的字节 */ public void write(byte[] buffer) { try { mmOutStream.write(buffer); // 把消息传给UI mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_WRITE, -1,-1, buffer).sendToTarget(); } catch (IOException e) { LogUtils.getInstance().e(getClass(), "Exception during write:" + e); } } public void cancel() { try { mmSocket.close(); } catch (IOException e) { LogUtils.getInstance().e(getClass(),"close() of connect socket failed:" + e); } } }
大概的流程就是上面三个线程里面所展现的,当然具体情况,根据项目来,比如蓝牙协议协议解析这块的根据协议定义的方式来进行解析;
代码中牵扯的到的蓝牙连接状态的改变,用到的handle,直接把状态发送至activity,通知activity更新;
/** * 无法连接,通知Activity */ private void connectionFailed() { setState(STATE_LISTEN); Message msg = mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(BluetoothChatActivity.TOAST, "无法连接设备"); msg.setData(bundle); mHandler.sendMessage(msg); } /** * 设备断开连接,通知Activity */ private void connectionLost() { Message msg = mHandler.obtainMessage(BluetoothChatActivity.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(BluetoothChatActivity.TOAST, "设备断开连接"); msg.setData(bundle); mHandler.sendMessage(msg); }
当点击发送按钮时,将文本输入框中的文字发送数据的方法:
private void sendMessage(String message) { if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) { Toast.makeText(this, R.string.not_connected,Toast.LENGTH_SHORT).show(); return; } if (message.length() > 0) { byte[] send = message.getBytes(); mChatService.write(send); } } //调用BluetoothChatService类中的write进行数据发送 public void write(byte[] out) { ConnectedThread r; synchronized (this) { if (mState != STATE_CONNECTED) return; r = mConnectedThread; } r.write(out); }
如此,蓝牙聊天的流程就是这样,如果退出聊天的时候,停止所有线程;
public synchronized void stop() { LogUtils.getInstance().e(getClass(), "---stop()"); setState(STATE_NONE); if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } if (mAcceptThread != null) { mAcceptThread.cancel(); mAcceptThread = null; } }
相信看完本篇文章,在安卓蓝牙连接这块应该问题不大了(spp协议)。
源码地址:点我查看源码