Android笔记—蓝牙串口(BlueTooth+EventBus)
资料来源如下
- 第一行代码(第二版)
编程环境
- Android Studio 2.2.3
导语
- 毕设中的蓝牙部分,记录以供复习
概述
最终效果
Android应用 与 Hc-05 蓝牙模块连接,单片机与 Android 端 可以通过串口正常收发数据.
预留足够灵活的接口, Android 应用中可以在任意位置获取到蓝牙数据
Activity切换时,蓝牙连接不断开.
用到的开源库/知识点资料 简介/来源
蓝牙:
Android API 指南https://developer.android.com/guide/topics/connectivity/bluetooth.html
EventBus:
EventBus-GitHub 是一个Android端优化的publish/subscribe消息总线,用来替代 Intent 、 Handler 、 Broadcast 等在 Actvity 、 Fragment 、 Service 等组件之间传递信息 . EventBus 可以传递一个完整的对象,简单高效, 注意 EventBus 只能在多线程之间传递消息,无法在不同进程之间传递消息 ,EventBus 3.0 以后进一步简化了传递方式,真的是很值得学习的一个开源库!参考资料如下:
EventBus 3.0初探: 入门使用及其使用 完全解析
EventBus3(3.0.0)源码解析
基础部分
- 蓝牙 与 EventBus 基础部分
BlueTooth
- 备注 : 这里使用的是 传统蓝牙 即 蓝牙4.0以前版本, 而不是 蓝牙4.0 ( ble低功耗蓝牙)及以后版本
- Android 对 Bluetooth 做了很好的封装,我们可以比较轻松的在 Android Bluetooth API 上进行开发.
- Android中所有蓝牙API均来自 android.bluetooth 包
BlueTooth基础
BluetoothAdapter 本地蓝牙适配器
BluetoothAdapter 是所有蓝牙交互的入口点, 在初始化蓝牙及蓝牙配对阶段使用
1.发现其他蓝牙设备
2.查询绑定(配对)设备的列表
3.使用已知的 MAC 地址实例化 BluetoothDevice
4.创建 BluetoothServerSocket 侦听来自其他设备的通信。BluetoothDevice 远程蓝牙设备
含有该设备的信息,例如设备的名称、地址、类和绑定状态等。BluetoothSocket 蓝牙套接字接口(与 TCP Socket 相似)
与 BluetoothDevice 配合建立远程连接,允许应用通过 InputStream 和 OutputStream 与其他蓝牙设备交换数据.
初始化蓝牙
声明蓝牙权限
Android 应用使用蓝牙前都需要声明蓝牙权限
一般只需要 BLUETOOTH 权限即可,但是考虑到之后有更多的需求,需要更改系统蓝牙设置,由此需要 BLUETOOTH_ADMIN 权限_也一起声明1
2
3
4
5<manifest ... >
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
...
</manifest>蓝牙设备可用性 获取 BluetoothAdapter
获取 BluetoothAdapter,需要静态 getDefaultAdapter() 方法。getDefaultAdapter() 会返回一个表示设备自身的蓝牙适配器的 BluetoothAdapter 设备不支持蓝牙 则返回 null1
2
3
4BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// 蓝牙不可用操作
}开启蓝牙
BluetoothAdapter 的 .isEnabled() 可以判断系统蓝牙是否开启.
没有开启时 startActivityForResult() 发送包含 ACTION_REQUEST_ENABLE 的 Intent 请求系统开启蓝牙,并在 onActivityResult() 返回的数据中 RESULT_CANCELED 表示开启失败 RESULT_OK 表示开启成功,这里我们只检测开启失败情况,并提示用户1
2
3
4
5
6
7
8
9
10
11
12
13
14if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_CANCELED) {
Toast.makeText(MyApplication.getContext(),R.string.Bluetooth_openfail, Toast.LENGTH_SHORT).show();
}
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
//设备不支持蓝牙时处理
}
}查询已配对的设备
调用 getBondedDevices(),返回已配对设备的一组 BluetoothDevice.之后使用for循环遍历1
2
3
4
5
6
7
8
9Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
//重新加载bluetoothDeviceList
bluetoothDeviceList.clear();
//BluetoothDevice列表循环
for (BluetoothDevice device : pairedDevices) {
bluetoothDeviceList.add(device);
}
}扫描设备
查找设备是非常耗费系统资源的事项,需要安排在子线程中执行,这里没有用到,由需要请参考Google官方蓝牙教程连接设备
蓝牙分主从机,链接为服务器/客户端。这里使用的是连接为客户端。- 首先获取远程设备的 BluetoothDevice 对象,即在选择设备阶段的BluetoothDevice
- 调用 createRfcommSocketToServiceRecord(UUID) 获取 BluetoothSocket,UUID通用唯一识别码 在蓝牙中具体是什么没有很好的解释。这里的值取的是
"00001101-0000-1000-8000-00805F9B34FB"
- 调用 connect() 发起连接,阻塞调用,需要在子线程中执行。
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//蓝牙连接子线程
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
//解析函数
ConnectThread(BluetoothDevice bluetoothDevice) {
// 使用一个中间变量 tmp
// mmSocket 类型是 final
BluetoothSocket tmp = null;
// 获取 BluetoothSocket
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(MyApplication.MY_UUID));
} catch (IOException ignored) {
}
//赋值给 mmSocket
mmSocket = tmp;
}
public void run() {
// 关闭蓝牙扫描
MyApplication.getBluetoothAdapter().cancelDiscovery();
try {
// 通过 socket 连接到设备. connect() 会一直执行直到成功连接或者抛出异常
mmSocket.connect();
} catch (IOException connectException) {
//无法连接到蓝牙,关闭连接并退出
try {
mmSocket.close();
}
//没有正常关闭
catch (IOException ignored) {
}
return;
}
// Do work to manage the connection (in a separate thread)
mConnectedThread = new ConnectedThread(mmSocket);
mConnectedThread.start();
}
/**
* 关闭蓝牙连接
*/
void cancel() {
try {
mmSocket.close();
} catch (IOException ignored) {
}
}
}
管理连接
获取BluetoothSocket
获取 InputStream 和 OutputStream, getInputStream() 和 getOutputStream() 来处理数据传输。read(byte[]) 和 write(byte[]) 读取数据并写入到流式传输。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
62private class ConnectedThread extends Thread {
//BluetoothSocket
private final BluetoothSocket mmSocket;
//输入流
private final InputStream mmInStream;
//输出流
private final OutputStream mmOutStream;
ConnectedThread(BluetoothSocket socket) {
//传入BluetoothSocket,实例化mmSocket
mmSocket = socket;
//输入/输出流 中间变量
InputStream tmpIn = null;
OutputStream tmpOut = null;
// 输入输出流实例化
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// 连接成功时
while (true) {
try {
// 在InputStream读数据
bytes = mmInStream.read();
//发送数据
Events.bluetooth_Recycle bluetooth_recycle = new Events.bluetooth_Recycle();
bluetooth_recycle.s = bytes;
bluetooth_recycle.bytes = String.valueOf((char) bytes);
EventBus.getDefault().post(bluetooth_recycle);
} catch (IOException e) {
break;
}
}
}
//写方法
void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭流连接
void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
}EventBus
直接放链接了
http://www.jianshu.com/p/a040955194fc
http://www.jianshu.com/p/acfe78296bb5
http://www.ff50.net/view/40565212977623506063.html
正文
- 蓝牙连接子线程
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
50private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
//解析函数
ConnectThread(BluetoothDevice bluetoothDevice) {
// 使用一个中间变量 tmp
// mmSocket 类型是 final
BluetoothSocket tmp = null;
// 获取 BluetoothSocket
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(MyApplication.MY_UUID));
} catch (IOException ignored) {
}
//赋值给 mmSocket
mmSocket = tmp;
}
public void run() {
// 关闭蓝牙扫描
MyApplication.getBluetoothAdapter().cancelDiscovery();
try {
// 通过 socket 连接到设备. connect() 会一直执行直到成功连接或者抛出异常
mmSocket.connect();
} catch (IOException connectException) {
//无法连接到蓝牙,关闭连接并退出
try {
mmSocket.close();
}
//没有正常关闭
catch (IOException ignored) {
}
return;
}
// Do work to manage the connection (in a separate thread)
mConnectedThread = new ConnectedThread(mmSocket);
mConnectedThread.start();
}
/**
* 关闭蓝牙连接
*/
void cancel() {
try {
mmSocket.close();
} catch (IOException ignored) {
}
}
}
管理连接子线程
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//管理连接子线程
private class ConnectedThread extends Thread {
//BluetoothSocket
private final BluetoothSocket mmSocket;
//输入流
private final InputStream mmInStream;
//输出流
private final OutputStream mmOutStream;
ConnectedThread(BluetoothSocket socket) {
//传入BluetoothSocket,实例化mmSocket
mmSocket = socket;
//输入/输出流 中间变量
InputStream tmpIn = null;
OutputStream tmpOut = null;
// 输入输出流实例化
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// 连接成功时
while (true) {
try {
// 在InputStream读数据
bytes = mmInStream.read();
//发送数据
Events.bluetooth_Recycle bluetooth_recycle = new Events.bluetooth_Recycle();
bluetooth_recycle.s = bytes;
bluetooth_recycle.bytes = String.valueOf((char) bytes);
EventBus.getDefault().post(bluetooth_recycle);
} catch (IOException e) {
break;
}
}
}
//写方法
void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭流连接
void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
}连接蓝牙
1
2
3
4
5
6
7
8
9//连接线程
private ConnectThread mConnectThread;
//管理连接进程
private ConnectedThread mConnectedThread;
//创建连接子线程
mConnectThread = new ConnectThread(bluetoothDevice);
//启动连接子线程
mConnectThread.start();向蓝牙写入数据
1
2
3
4
5
6
7//发送数据String, 主线程 优先级4 非粘性事件
public void onEvent(Events.bluetooth_Send bluetoothSend) {
String bytes = bluetoothSend.bytes;
mConnectedThread.write(bytes.getBytes());
LogUtil.d(Tag, "onEvent:Send" + bluetoothSend.bytes);
}接收数据处理
1
2
3
4
5
6//数据转换 后台进程 优先级3 非粘性事件
public void onEvent(Events.bluetooth_Transformers bluetoothTransformers) {
int bytes = bluetoothTransformers.bytes;
//处理数据
}