史上最全系列之网络通信之蓝牙

一、蓝牙介绍
Android蓝牙协议栈使用的是BlueZ,支持GAP, SDP, and RFCOMM规范,是一个SIG认证的蓝牙协议栈。
Bluez 是GPL许可的,因此Android的框架内与用户空间的bluez代码通过D-BUS进程通讯进行交互,以避免专有代码。
Headset和Handsfree(v1.5)规范就在Android框架中实现的,它是跟Phone App紧密耦合的。这些规范也是SIG认证的。
Android蓝牙库结构图:



Android蓝牙进程结构图:

Android蓝牙通信结构图


Android从2.0开始,正式支持蓝牙,在此之前只能通过第三方API完成关于蓝牙的应用程序设计。另外,模拟器不支持蓝牙,只能用真机进行相关测试,当然也可以用电脑与真机配合进行测试,电脑端程序借助bluecove提供的jar包可以完成。

二、应用蓝牙完成的工作
1、扫描其他Bluetooth设备。
2、查询配对Bluetooth设备的本地Bluetooth适配器。
3、建立RFCOMM通道。
4、通过服务探索连接到其他设备。
5、与其他设备进行数据传输。
6、管理多个连接。
三、常用蓝牙API简介
表名 常用蓝牙API简介    
版本 X1    
类名 方法 方法的作用 类的作用
BluetoothAdapter cancelDiscovery() 取消搜索设备 蓝牙适配器获取本地蓝牙设备
  disable() 关闭蓝牙设备  
  enable() 打开蓝牙设备  
  getAddress() 获取本地蓝牙地址  
  getDefaultAdapter() 获取默认蓝牙适配器  
  getName() 获取本地蓝牙名称  
  getRemoteDevice(String address) 根据蓝牙地址获取远程蓝牙设备  
  getState() 获取本地蓝牙设备状态  
  isDiscovering() 判断当前是否正在搜索设备  
  isEnabled() 判断蓝牙是否打开  
  listenUsingRfcommWithServiceRecord(String name,UUID uuid) 根据名称,UUID创建并返回BluetoothServerSocket  
  startDiscovery() 开始搜索设备  
BluetoothDevice createRfcommSocketToServiceRecord(UUIDuuid) 根据UUID创建并返回一个BluetoothSocket 蓝牙设备管理获取远程设备
  getAddress() 获取远程蓝牙地址  
  getName() 获取远程蓝牙名称  
BluetoothServerSocket accept() 接收客户端连接请求,会阻塞线程 蓝牙服务器Socket
  accept(inttimeout) 接收客户端连接请求并指定过时时间,会阻塞线程  
  close() 关闭连接接收  
BluetoothSocket close() 关闭连接 客户端Socket
  connect() 建立连接  
  getInptuStream() 获取输入流  
  getOutputStream() 获取输出流  
  getRemoteDevice() 获取远程设备  

四、蓝牙权限
?
代码片段,双击复制
01
02
< uses-permission android:name = "android.permission.BLUETOOTH_ADMIN" />
< uses-permission android:name = "android.permission.BLUETOOTH" />

五、蓝牙建立流程
1.使用registerReceiver注册BroadcastReceiver来获取蓝牙状态、搜索设备等消息。
2.使用BlueAdatper的搜索。
3.在BroadcastReceiver的onReceive()里取得搜索所得的蓝牙设备信息(如名称,MAC,RSSI)。
4.通过设备的MAC地址来建立一个BluetoothDevice对象。
5.由BluetoothDevice衍生出BluetoothSocket,准备SOCKET来读写设备。
6.通过BluetoothSocket的createRfcommSocketToServiceRecord()方法来选择连接的协议/服务,这里用的是SPP(UUID:00001101-0000-1000-8000-00805F9B34FB)。
7.Connect之后(如果还没配对则系统自动提示),使用BluetoothSocket的getInputStream()和getOutputStream()来读写蓝牙设备。
六、Broadcast使用Action介绍
公用Action
表名 Action作用
版本 X1
ACTION_STOP_SERVICE 关闭后台服务。当程序退出或需要停止蓝牙服务时发送此广播
ACTION_DATA_TO_SERVICE 数据传送至后台Service。包含一个key为DATA的参数,该参数类型为实现了Serializable接口的类(该类为用户自己编写的数据实体类)。
ACTION_CONNECT_SUCCESS 连接成功。从后台Service发送出连接成功建立的广播。
ACTION_CONNECT_ERROR 连接错误。从后台Serivce发送出连接发生错误的广播。
ACTION_DATA_TO_GAME 从后台Service传送出数据。包含一个key为DATA的参数,该参数类型为实现了Serializable接口的类(该类为用户自己编写的数据实体类)。
ACTION_START_DISCOVERY 开启蓝牙搜索。命令后台Service开始蓝牙搜索。
ACTION_SELECTED_DEVICE 选中的蓝牙设备。包含一个key为DEVICE的参数,该参数类型为BluetoothDevice(蓝牙设备类)。
ACTION_FOUND_DEVICE 发现设备。后台Service进行搜索蓝牙设备过程中,每发现一个设备便会发送该Broadcast。
ACTION_NOT_FOUND_SERVER 未发现服务器设备。后台Service通过搜索并未发现可连接的蓝牙设备,发送此Broadcast。

七、蓝牙例程分析
XML布局代码
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
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
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:tools = "http://schemas.android.com/tools"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:orientation = "horizontal" >
 
< LinearLayout
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:layout_weight = "1"
android:orientation = "vertical" >
 
< ListView
android:id = "@+id/deviceList"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content" >
</ ListView >
</ LinearLayout >
 
< LinearLayout
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:layout_weight = "2"
android:orientation = "vertical" >
 
< LinearLayout
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:orientation = "horizontal" >
 
< TextView
android:id = "@+id/stateTxt"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
android:text = "@string/state_text"
android:focusable = "true" />
 
< ToggleButton
android:id = "@+id/stateTBtn"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
android:textOn = "@string/open"
android:textOff = "@string/close"
android:clickable = "false" />
</ LinearLayout >
 
< Button
android:id = "@+id/openBtn"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:gravity = "center"
android:text = "@string/open_text" />
 
< Button
android:id = "@+id/closeBtn"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:gravity = "center"
android:text = "@string/close_text" />
 
< Button
android:id = "@+id/discoverBtn"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:gravity = "center"
android:text = "@string/discover_text" />
 
< LinearLayout
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:orientation = "horizontal" >
 
< EditText
android:id = "@+id/sendedtTxt"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
android:gravity = "center"
android:singleLine = "true"
android:hint = "@string/message"
android:textSize = "12sp" />
 
< Button
android:id = "@+id/sendBtn"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:layout_weight = "1"
android:gravity = "center"
android:text = "@string/send_text"
android:textSize = "12sp" />
</ LinearLayout >
 
< ListView
android:id = "@+id/chatList"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content" >
</ ListView >
</ LinearLayout >
 
</ LinearLayout >

实现关键代码
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
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
//客户端线程,连接蓝牙设备
private class clientThread extends Thread {
public void run() {
try {
//创建一个Socket连接:只需要服务器在注册时的UUID号
device=local.getRemoteDevice(BlueToothAddress);
socket = device.createRfcommSocketToServiceRecord(UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB" ));
Message msg2 = new Message();
msg2.obj = "请稍候,正在连接服务器:" +BlueToothAddress;
msg2.what = 0 ;
LinkDetectedHandler.sendMessage(msg2);
System.out.println(socket+ "|mac:" +BlueToothAddress);
socket.connect();
 
Message msg = new Message();
msg.obj = "已经连接上服务端!可以发送信息。" ;
msg.what = 0 ;
LinkDetectedHandler.sendMessage(msg);
//启动接受数据
mreadThread = new readThread();
mreadThread.start();
}
catch (IOException e)
{
System.out.println( "异常" );
Message msg = new Message();
msg.obj = "连接服务端异常!断开连接重新试一试。" ;
msg.what = 0 ;
LinkDetectedHandler.sendMessage(msg);
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
 
//处理已发现设备
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//获取发现的设备对象
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//跳过已配对的设备
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
delist.add( new SiriListItem(device.getName() + "undefined"
+ device.getAddress(), false ));
mAdapter.notifyDataSetChanged();
deviceList.setSelection(delist.size() - 1 );
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
.equals(action)) {
setProgressBarIndeterminateVisibility( false );
if (deviceList.getCount() == 0 ) {
delist.add( new SiriListItem( "没有发现蓝牙设备" , false ));
mAdapter.notifyDataSetChanged();
deviceList.setSelection(list.size() - 1 );
}
discoverBtn.setText( "重新搜索" );
}
}
};
 
// 发送数据
private void sendMessageHandle(String msg) {
if (socket == null ) {
Toast.makeText(mContext, "没有连接" , Toast.LENGTH_SHORT).show();
return ;
}
try {
OutputStream os = socket.getOutputStream();
os.write(msg.getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
list.add( new deviceListItem(msg, false ));
mAdapter.notifyDataSetChanged();
chatList.setSelection(list.size() - 1 );
}
//读取数据
private class readThread extends Thread {
public void run() {
 
byte [] buffer = new byte [ 1024 ];
int bytes;
InputStream mmInStream = null ;
String tmp = null ;
try {
mmInStream = socket.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while ( true ) {
try {
//读取输入流中的数据
if ((bytes = mmInStream.read(buffer)) > 0 ) {
 
for ( int i = 0 ; i < bytes; i++) {
tmp = "" +buffer<i>;
String st = new String(tmp);
tmp = null ;
Message msg = new Message();
msg.obj = st;
msg.what = 1 ;
LinkDetectedHandler.sendMessage(msg);
}
}
} catch (IOException e) {
try {
mmInStream.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
break ;
}
}
}
}</i>

八、关于UUID的介绍

简介
UUID含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OSF) 的组织应用在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部分。
作用
UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。目前最广泛应用的 UUID,即是微软的 Microsoft's Globally Unique Identifiers (GUIDs),而其他重要的应用,则有 Linux ext2/ext3 档案系统、LUKS 加密分割区、GNOME、KDE、Mac OS X 等等。
组成
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字
UUID由以下几部分的组合:
(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列。
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。

#Android蓝牙串口服务UUID
SerialPortServiceClass_UUID = ‘{00001101-0000-1000-8000-00805F9B34FB}’
附上例程代码:
 TestBluetooth.zip (1.04 MB, 下载次数: 92) 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值