• 欢迎访问风的记忆博客网站,如有疑问请加作者QQ或者微信联系。作者QQ:524100248,微信号:sendtion。

Android蓝牙开发全面总结

安卓教程 sendtion 9年前 (2016-03-23) 2399次浏览 已收录 0个评论 扫描二维码
文章目录[隐藏]

来源:AndroidChina

基本概念

安卓平台提供对蓝牙的通讯栈的支持,允许设别和其他的设备进行无线传输数据。应用程序层通过安卓 API 来调用蓝牙的相关功能,这些 API 使程序无线连接到蓝牙设备,并拥有 P2P 或者多端无线连接的特性。

蓝牙的功能:

1、扫描其他蓝牙设备

2、为可配对的蓝牙设备查询蓝牙适配器

3、建立 RFCOMM 通道(其实就是尼玛的认证)

4、通过服务搜索来链接其他的设备

5、与其他的设备进行数据传输

6、管理多个连接

蓝牙建立连接必须要求:

1、打开蓝牙

2、查找附近已配对或可用设备

3、连接设备

4、设备间数据交互

首先,要操作蓝牙,先要在 AndroidManifest.xml 里加入权限

1
2
1
<
1
uses-permissionandroid:name
1
=
1
"android.permission.BLUETOOTH_ADMIN"

 

1
/>
1
<
1
uses-permissionandroid:name
1
=
1
"android.permission.BLUETOOTH"

 

1
/>

Android 所有关于蓝牙开发的类都在 android.bluetooth 包下,只有 8 个类

1.BluetoothAdapter 蓝牙适配器

直到我们建立 bluetoothSocket 连接之前,都要不断操作它 BluetoothAdapter 里的方法很多,常用的有以下几个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
cancelDiscovery() 根据字面意思,是取消发现,也就是说当我们正在搜索设备的时候调用这个方法将不再继续搜索
1
disable()关闭蓝牙
1
enable()打开蓝牙,这个方法打开蓝牙不会弹出提示,更多的时候我们需要问下用户是否打开,一下这两行代码同样是打开蓝牙,不过会提示用户:
1
Intemtenabler=
1
new

 

1
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
1
startActivityForResult(enabler,reCode);
1
//同startActivity(enabler);
1
getAddress()获取本地蓝牙地址
1
getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter
1
getName()获取本地蓝牙名称
1
getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备
1
getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
1
isDiscovering()判断当前是否正在查找设备,是返回
1
true
1
isEnabled()判断蓝牙是否打开,已打开返回
1
true
1
,否则,返回
1
false
1
listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步
1
startDiscovery()开始搜索,这是搜索的第一步
1
使用BluetoothAdapter的startDiscovery()方法来搜索蓝牙设备
1
startDiscovery()方法是一个异步方法,调用后会立即返回。该方法会进行对其他蓝牙设备的搜索,该过程会持续
1
12
1
秒。该方法调用后,搜索过程实际上是在一个System Service中进行的,所以可以调用cancelDiscovery()方法来停止搜索(该方法可以在未执行discovery请求时调用)。
1
请求Discovery后,系统开始搜索蓝牙设备,在这个过程中,系统会发送以下三个广播:
1
ACTION_DISCOVERY_START:开始搜索
1
ACTION_DISCOVERY_FINISHED:搜索结束
1
ACTION_FOUND:找到设备,这个Intent中包含两个extra fields:EXTRA_DEVICE和EXTRA_CLASS,分别包含BluetooDevice和BluetoothClass。
1
我们可以自己注册相应的BroadcastReceiver来接收响应的广播,以便实现某些功能

2.BluetoothDevice 描述了一个蓝牙设备

1
2
3
4
1
createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket
1
getState() 蓝牙状态这里要说一下,只有在 BluetoothAdapter.STATE_ON 状态下才可以监听,具体可以看andrid api;
1
这个方法也是我们获取BluetoothDevice的目的――创建BluetoothSocket
1
这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter

3.BluetoothServerSocket

这个类一种只有三个方法两个重载的 accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!

还有一点需要注意的是,这两个方法都返回一个 BluetoothSocket,最后的连接也是服务器端与客户端的两个 BluetoothSocket 的连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
void

   

1
close()
1
Closes the object and release any system resources it holds.
1
void

   

1
connect()
1
Attempt to connect to a remote device.
1
InputStream getInputStream()
1
Get the input stream associated with 
1
this

 

1
socket.
1
OutputStream    getOutputStream()
1
Get the output stream associated with 
1
this

 

1
socket.
1
BluetoothDevice getRemoteDevice()
1
Get the remote device 
1
this

 

1
socket is connecting, or connected, to.
1
获取远程设备,该套接字连接,或连接到---。
1
boolean
1
isConnected()
1
Get the connection status of 
1
this

 

1
socket, ie, whether there is an active connection with remote device.
1
判断当前的连接状态

4.BluetoothSocket

跟 BluetoothServerSocket 相对,是客户端一共 5 个方法,不出意外,都会用到

1
2
3
4
5
1
close(),关闭
1
connect()连接
1
getInptuStream()获取输入流
1
getOutputStream()获取输出流
1
getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

5、BluetoothClass

代表一个描述了设备通用特性和功能的蓝牙类。比如,一个蓝牙类会指定皆如电话、计算机或耳机的通用设备类型,可以提供皆如音频或者电话的服务。

每个蓝牙类都是有 0 个或更多的服务类,以及一个设备类组成。设备类将被分解成主要和较小的设备类部分。

BluetoothClass 用作一个能粗略描述一个设备(比如关闭用户界面上一个图标的设备)的线索,但当蓝牙服务事实上是被一个设备所支撑的时候,BluetoothClass 的 介绍则不那么可信任。精确的服务搜寻通过 SDP 请求来完成。当运用 createRfcommSocketToServiceRecord(UUID) 和 listenUsingRfcommWithServiceRecord(String, UUID)来创建 RFCOMM 端口的时候,SDP 请求就会自动执行。

使用 getBluetoothClass()方法来获取为远程设备所提供的类。

两个内部类:

class   BluetoothClass.Device

定义所有设备类的常量

class   BluetoothClass.Service

定义所有服务类的常量

公共方法:

public int describeContents ()

描述包含在可封装编组的表示中所有特殊对象的种类。

返回值 

一个指示被 Parcelabel 所排列的特殊对象类型集合的位掩码。

public boolean equals (Object o)

比较带有特定目标的常量。如果他们相等则标示出来。 为了保证其相等,o 必须代表相同的对象,该对象作为这个使用类依赖比较的常量。通常约定,该比较既要可移植又需灵活。

当且仅当 o 是一个作为接收器(使用==操作符来做比较)的精确相同的对象是,这个对象的实现才返回 true 值。子类通常实现 equals(Object)方法,这样它才会重视这两个对象的类型和状态。

通常约定,对于 equals(Object)和 hashCode() 方法,如果 equals 对于任意两个对象返回真值,那么 hashCode()必须对这些对象返回相同的纸。这意味着对象的子类通常都覆盖或者都不覆盖这两个方法。

参数

o   需要对比常量的对象

返回值

如果特定的对象和该对象相等则返回 true,否则返回 false。

public int getDeviceClass ()

返回 BluetoothClass 中的设备类部分(主要的和较小的)

从函数中返回的值可以和在 BluetoothClass.Device 中的公共常量做比较,从而确定哪个设备类在这个蓝牙类中是被编码的。

返回值

设备类部分

public int getMajorDeviceClass ()

返回 BluetoothClass 中设备类的主要部分

从函数中返回的值可以和在 BluetoothClass.Device.Major 中的公共常量做比较,从而确定哪个主要类在这个蓝牙类中是被编码的。

返回值

主要设备类部分

public boolean hasService (int service)

如果该指定服务类被 BluetoothClass 所支持,则返回 true

在 BluetoothClass.Service 中,合法的服务类是公共常量,比如 AUDIO 类。

参数

service 合法服务类

返回值

如果该服务类可被支持,则返回 true

public int hashCode ()

返回这个对象的整型哈希码。按约定,任意两个在 equals(Object)中返回 true 的对象必须返回相同的哈希码。这意味着对象的子类通常通常覆盖或者都不覆盖这两个方法。

注意:除非同等对比信息发生改变,否则哈希码不随时间改变而改变。

public String toString ()  

返回这个对象的字符串,该字符串包含精确且可读的介绍。系统鼓励子类去重写该方法,并且提供了能对该对象的类型和数据进行重视的实现方法。默认的实现方法只是简单地把类名、“@“符号和该对象 hashCode()方法的 16 进制数连接起来(如下列所示的表达式):

1
2
3
4
5
1
public

 

1
void

 

1
writeToParcel (Parcel out, 
1
int

 

1
flags)
1
将类的数据写入外部提供的Parcel中
1
参数
1
out     对象需要被写入的Parcel
1
flags  和对象需要如何被写入有关的附加标志。可能是
1
0
1
,或者可能是PARCELABLE_WRITE_RETURN_VALUE。

以上是蓝牙主要操作的类。

基本用法:

1、获取本地蓝牙适配器

1
1
BluetoothAdapter mAdapter= BluetoothAdapter.getDefaultAdapter();

2、打开蓝牙

1
2
3
4
5
6
7
1
if
1
(!mAdapter.isEnabled()){
1
//弹出对话框提示用户是后打开
1
Intent enabler = 
1
new

 

1
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
1
startActivityForResult(enabler, REQUEST_ENABLE);
1
//不做提示,强行打开,此方法需要权限<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
1
// mAdapter.enable();
1
}

3、搜索设备

1)mAdapter.startDiscovery() 是第一步,可能你会发现没有返回的蓝牙设备

2)定义 BroadcastReceiver,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
// 创建一个接收ACTION_FOUND广播的BroadcastReceiver
1
private

 

1
final

 

1
BroadcastReceiver mReceiver = 
1
new

 

1
BroadcastReceiver() {
1
     
1
public

 

1
void

 

1
onReceive(Context context, Intent intent) {
1
          
1
String action = intent.getAction();
1
          
1
// 发现设备
1
          
1
if

 

1
(BluetoothDevice.ACTION_FOUND.equals(action)) {
1
               
1
// 从Intent中获取设备对象
1
               
1
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1
               
1
// 将设备名称和地址放入array adapter,以便在ListView中显示
1
               
1
mArrayAdapter.add(device.getName() + 
1
"/n"

 

1
+ device.getAddress());
1
          
1
}
1
else

 

1
if

 

1
(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
1
               
1
if

 

1
(mNewDevicesAdapter.getCount() == 
1
0
1
) {
1
                    
1
Log.v(TAG, 
1
"find over"
1
);
1
               
1
}
1
         
1
}
1
    
1
};
1
// 注册BroadcastReceiver
1
IntentFilter filter = 
1
new

 

1
IntentFilter(BluetoothDevice.ACTION_FOUND);
1
registerReceiver(mReceiver, filter); 
1
// 不要忘了之后解除绑定

4、建立连接

首先 Android sdk(2.0 以上版本)支持的蓝牙连接是通过 BluetoothSocket 建立连接,服务器端(BluetoothServerSocket)和客户端(BluetoothSocket)需指定同样的 UUID,才能建立连接,因为建立连接的方法会阻塞线程,所以服务器端和客户端都应启动新线程连接
1)服务器端:

1
2
3
4
1
//UUID格式一般是"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"可到
1
//<a href="http://www.uuidgenerator.com%20/">http://www.uuidgenerator.com </a>申请
1
BluetoothServerSocket serverSocket = mAdapter. listenUsingRfcommWithServiceRecord(serverSocketName,UUID);
1
serverSocket.accept();

2)客户端:

1
2
3
1
//通过BroadcastReceiver获取了BLuetoothDevice
1
BluetoothSocket clienSocket=dcvice. createRfcommSocketToServiceRecord(UUID);
1
clienSocket.connect();

5、数据传递

通过以上操作,就已经建立的 BluetoothSocket 连接了,数据传递是通过流

1)获取流

1
2
1
inputStream = socket.getInputStream();
1
    
1
outputStream = socket.getOutputStream();

2)写出、读入(JAVA 常规操作)

补充一下,使设备能够被搜索

1
2
1
Intent enabler = 
1
new

 

1
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
1
    
1
startActivityForResult(enabler,REQUEST_DISCOVERABLE);

关于蓝牙连接:

可以直接用以下代码进行连接:

1
2
3
4
5
6
1
final

 

1
String SPP_UUID = 
1
"00001101-0000-1000-8000-00805F9B34FB"
1
;
1
UUID uuid = UUID.fromString(SPP_UUID);
1
BluetoothSocket socket;
1
socket = device.createInsecureRfcommSocketToServiceRecord(uuid);
1
adapter.cancelDiscovery();
1
socket.connect();

1.startDiscovey 有可能启动失败

一般程序中会有两步:开启蓝牙、开始寻找设备。顺序执行了开启蓝牙-寻找设备的步骤,但是由于蓝牙还没有完全打开,就开始寻找设备,导致寻找失败。

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
adapter = BluetoothAdapter.getDefaultAdapter();
1
if

 

1
(adapter == 
1
null
1
)      {
1
// 设备不支持蓝牙
1
}
1
// 打开蓝牙
1
if

 

1
(!adapter.isEnabled()){
1
    
1
adapter.enable();
1
    
1
adapter.cancelDiscovery();
1
}
1
// 寻找蓝牙设备,android会将查找到的设备以广播形式发出去
1
while

 

1
(!adapter.startDiscovery()){
1
    
1
Log.e(
1
"BlueTooth"
1
1
"尝试失败"
1
);
1
    
1
try

 

1
{
1
        
1
Thread.sleep(
1
100
1
);
1
    
1
1
catch

 

1
(InterruptedException e) {
1
        
1
e.printStackTrace();
1
    
1
}
1
}

2.接收数据转换

使用 socket.getInputStream 接收到的数据是字节流,这样的数据是没法分析的,所以很多情况需要一个 byte 转十六进制 String 的函数:

1
2
3
4
5
6
7
8
9
1
public
1
static
1
String bytesToHex(
1
byte
1
[] bytes) {
1
    
1
char
1
[] hexChars = 
1
new
1
char
1
[bytes.length * 
1
2
1
];
1
    
1
for
1
1
int
1
j = 
1
0
1
; j &amp;lt; bytes.length; j++ ) {
1
        
1
int
1
v = bytes[j] &amp;amp; 
1
0xFF
1
;
1
        
1
hexChars[j * 
1
2
1
] = hexArray[v &amp;gt;&amp;gt;&amp;gt; 
1
4
1
];
1
        
1
hexChars[j * 
1
2
1
1
1
1
] = hexArray[v &amp;amp; 
1
0x0F
1
];
1
    
1
}
1
    
1
return
1
new
1
String(hexChars);
1
}

 

 


风的记忆 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Android 蓝牙开发全面总结
喜欢 (0)
[sendtion@126.com]
分享 (0)
sendtion
关于作者:
一个不断奋斗追逐梦想的少年~
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址