Android蓝牙CS通信机制的深入挖掘与使用

来源:互联网 发布:鹰扬拜占庭 知乎 编辑:程序博客网 时间:2024/06/05 09:59

Android蓝牙CS通信机制的深入挖掘与使用

个人说明:以下所用到的资料均来源于网络,笔者会在重要部分标明出处。本文不涉及任何公司的机密文件。鉴于个人水平有限,如果读者发现有任何错误之处,希望读者能与本人联系。因为我个人CSDN的博客不知道为什么上不去了,为了美观,代码选择了贴图。如果需要文中任何代码可以联系我chlianghui@gmail.com.

前言

蓝牙是目前为止手机近距离通信的主要方法,Android的蓝牙功能的成长我们也有目共睹。接下来我将通过一个小小的基于Client Server的数据通信的例子讲解蓝牙传输的方方面面,包括Android蓝牙基本知识、蓝牙服务、自定义服务的使用这三个大方面。

一、Android蓝牙基本知识。

关于蓝牙操作的基本知识网络上和书很多地方都有讲解,本文主要参考了网上比较经典的http://www.eoeandroid.com/thread-18993-1-1.html这篇文章,大家可以先看看,我们这里以补充为主。而和蓝牙有关的类源代码可到http://www.google.com/codesearch上面搜索有关源代码。

1.Android中使用蓝牙开发前要加上权限:android.permission.BLUETOOTH和android.permission.BLUETOOTH_ADMIN

2.对于操作手机蓝牙功能的部分,如打开蓝牙、可见、搜索、配对连接,等,我们主要用到BluetoothAdapter和BluetoothDevice这两个类。

Adapter代表的是本机上的蓝牙模块,故此类一般是跟本机有关的内容。

而Device代表得则是蓝牙搜索到的一个远端设备,故此类一般是与针对远端设备进行操作有关的内容。

3.对于蓝牙的数据操作功能的部分,我们需要用到BluetoothSocket和BluetoothServerSocket这两个类。

接下来我们在我们的示例程序中应用以上的类让两部手机完成一次配对连接。假设我们有两部手机,一部为Client,一部为Server。打开蓝牙的代码省略。

Server主要代码段如下:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

图1.1 Server注册服务的主要代码

Client主要代码段如下:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

图1.2 Client连接服务的主要代码

二、蓝牙服务

首先介绍一下UUID,在蓝牙中,每个服务和服务属性都唯一地由"全球唯一标识符" (UUID)来校验。正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。UUID类可表现为短整形(16或32位)和长整形(128 位)UUID。他提供了分别利用String和16位或32位数值来创建类的构造函数,提供了一个可以比较两个UUID(如果两个都是128位)的方法,还有一个可以转换一个UUID为一个字符串的方法。(原创未找到,引用自http://www.bitscn.com/pdb/java/200605/22644.html )

众所周知,在我们的TCP/IP协议下,每个端口都可以对应一种服务,例如HTTP协议对应80这个端口。而在Bluetooth协议下,同样的,一个蓝牙模块服务也对应着一个端口。在蓝牙设备用BluetoothAdapter的listenUsingRfcommWithServiceRecord的方法注册一个服务的时候,系统就会为之分派一个端口。我们可以通过查看BluetoothAdapter的源代码研究其原理:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

图2.1 listenUsingRFcommWithServiceRecord代码段1

可以看到该函数先用传入的UUID作为参数创建了一个叫RfcommChannelPicker的类,我们不妨顾名思义,先入为主,认为它就是针对UUID分配查找channel的一个类。追根溯源,看看它里面有些什么:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.2 RfcommChannelPicker的类成员

首先我们看看类成员,映入眼帘的是4个排列得很整齐的数字组成的数组reserved,而且后面还有注释,咱们谷歌百度搜搜搜狗或有道一下,HFAG(handsfree audio gateway)可能是跟蓝牙免提服务有关,HSAG(headset audio gateway)可能跟蓝牙耳机服务有关,而OPUSH(Object Push)就是OPP服务,通过这个服务我们可以推送对象到另一部有这个服务的手机,而PBAP(PhoneBook Access)则是提供通过蓝牙传送手机电话本的一个服务协议。而它们都对应着数字,是否意味着这些蓝牙服务对应的channel已经是被系统保留了呢?(就像http是80一样),我们需要进一步确认。

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.3 RfcommChannelPicker的构造函数和nextChannel函数

首先我们可以看到程序将一串连续的数字加入到sChannel这个list中,通过查看,我们得到MAX_RFCOMM_CHANNEL数值为30,而后,又把sChannel中,与reserved数组中相等的元素remove了。然后将可用的channel数字克隆到mChannels中,在nextChannel函数中,我们可以知道程序通过random的方法来给uuid配对channel。

最后我们发现在listenUsingRFcommWithServiceRecord中有这样的一段代码:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.4 listenUsingRFcommWithServiceRecord代码

而BluetoothServerSocket的原型是:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图 2.5 BluetoothServerSocket的原型

至此可知,当我们使用BluetoothAdapter的listenUsingRFcommWithServiceRecord创建一个服务的时候,系统会为其分配一个channel(其实就是port),然后调用native的函数,创建一个socket。我们如果用C对Android进行开发的话,要建立服务就要注意使用port。

那么,我们现在知道UUID其实也是对应着port的服务而已,那么当我们搜索到Device之后,怎么知道Device上究竟提供了什么服务?

我们翻查BluetoothDevice,发现有一个这样的函数:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.6 getuuid函数

这个看起来好像是我们需要的函数,虽然它被hide了,但是我们尝试着用反射机制调用之。代码如下:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.7 获得远端蓝牙设备提供服务的方法

其中device是我们要查找服务的设备。我用这段代码查查咱们的“国产神机”M某吧。

结果在logcat中显示:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

图2.8 M9提供的蓝牙服务

Run了上面一段程序后得到这个UUID list,其中0000112f-0000-1000-8000-00805f9b34fb就是刚才我们提到过的PBAP服务的UUID。如果用这段代码搜没配过对的手机,得到的结果为空,因为这个getUuids的真身其实是BluetoothService类里面的getUuidFromCache(对getUuids进行追根溯源),是在本机与远端机配对连接的过程中保存下来的。

现在我们用以下代码在M9上面注册一个服务:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.9 为M9注册蓝牙服务

其中所用到的UUID是用UUIDMaker生成的。(第三部分会讲)然后测试图2.6代码段的手机与M9先将配对状态取消掉,重新配对一次,再运行图2.6的代码段,我们可以看到以下的结果:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图2.10  M9提供的蓝牙服务2

可以看到我们注册的服务已经能成功被搜索到了!这就是为什么我们调用BluetoothDevice的createRfcommSocketToServiceRecord函数能连接上对方提供的服务的原因。而createRfcommSocketToServiceRecord里面又是怎样的原理,可以用同样的方法去深挖。

三、自定义服务蓝牙传输的实现

既然我们弄懂了这个蓝牙服务其实是怎么回事,那么我们就可以自己创建自己的服务了哦!好,先别激动,一种服务应该对应一种协议,就像去饮料店,你需要饮料,然后给钱,然后店员给饮料,这就是饮料服务的一种无形的协议。

1.自定义协议

首先确定我们要做什么,相信做蓝牙开发的人一定看过BluetoothChat这个经典的例子,它功能只限于传输文字,那么我们要改进它,要加入在聊天的过程中传输文件的功能应怎么做?我们定义这样的一种协议(假设双方都已经建立好连接):

a.发送信息方先发一个标志,说明我接下来要发的是普通的聊天字符还是文件,例如0xEE代表“我要说话”,0xEF代表“我要发文件”。

b.然后发送方再发接下来内容的长度,假设要发字符(这时候程序已经在UI层获取了发送人说的话)长度为10个字节,就发0x0A,假设要发的文件是11个字节大小,就发0x0B。然后发送人就根据需要发送内容。

c.接收方收到首个字节,知道接下来是什么内容。然后再收第二个字节知道内容的长度。然后接收这个长度的内容。根据内容的类型(首字节),决定是显示在聊天界面还是存储到存储介质上。

以上简单的几步就是一个自定义的服务协议。

2.创建属于自己的UUID

为了不让自己创造的服务不与其他人的冲突,所以一定要注册一个全球唯一的uuid,这里有一款工具UUIDMaker,上网搜就搜到,而且使用非常简单。界面如下:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图3.1UUIDMAker界面

在协议和UUID都完成后,编写代码就事半功倍了。

Server端的核心代码:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图3.2 Server核心源代码

Client端的核心代码:

Android蓝牙CS通信机制的深入挖掘与使用 - lighthearts - lighthearts的博客

 图3.3 Client核心源代码

另外需要说明的是,在第二部分中我们看到M9还有很多系统提供的服务,例如PBAP、OPP等,我们一样可以用以上客户端的方法来使用这些服务,但是首先我们需要清楚这些服务的工作原理,就要首先阅读相关的资料和查看源代码。

四、总结

本文主要针对蓝牙服务注册原理、使用方法进行了比较深入的讲解,让开发者对蓝牙CS方式下的通信操作更加了解。笔者认为,研究Android的源代码是Android开发者提升自己开发水平最好的方法,而阅读加测试的方法则令研究的效果事半功倍。



 


原创粉丝点击