Android实现蓝牙聊天功能

时间:2021-12-18 21:31:39

蓝牙,时下最流行的智能设备传输数据的方式之一,通过手机app和智能设备进行连接,获取设备上的测量数据,我们生活中随处可见的比如蓝牙智能手环,蓝牙电子秤,蓝牙心电测量设备等等。

本篇我将紧接着上篇结尾所写,一起来看下手机之间如何通过蓝牙实现文字聊天。

先贴出上篇的一些demo;

Android实现蓝牙聊天功能

当点击图上的两个列表中的任何一个列表,执行如下代码:

?
1
2
3
4
5
6
7
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();

此蓝牙聊天工具最后实现的效果是这样的:

Android实现蓝牙聊天功能

将回到聊天主界面:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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.开一个线程获监听蓝牙传入的连接,如果连接被接受的话,再开启第三个线程去处理所有传入和传出的数据;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

开线程去连接

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * @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);
 }
 }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
 * 监听传入的连接
 */
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更新;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 无法连接,通知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);
}

当点击发送按钮时,将文本输入框中的文字发送数据的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}

如此,蓝牙聊天的流程就是这样,如果退出聊天的时候,停止所有线程;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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协议)。

源码地址:点我查看源码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/paul_zzq/article/details/70799872