Android 平台包含蓝牙网络堆栈支持,凭借此支持,设备能以无线方式与其余蓝牙设备交换数据。应用框架提供了经过 Android Bluetooth API 访问蓝牙功能的途径。使用 Bluetooth API Android 应用能够执行下面的操做:java
传统蓝牙适用于电池使用强度较大的操做,例如 Android 设备之间的流传输和通讯等。针对具备低功耗要求的蓝牙设备,Android 4.3(API 18)中引入了面向低功耗蓝牙的 API 支持。android
使用 Android Bluetooth API 来完成使用蓝牙进行通讯的四项主要任务:设置蓝牙、查找局部区域内的配对设备或可用设备、链接设备,以及在设备之间传输数据。数据库
关于蓝牙的 API 在 android.bluetooth
包中,下面介绍一下和蓝牙相关的主要类服务器
BluetoothAdapter网络
本地蓝牙适配器,是全部蓝牙交互的入口点,表示蓝牙设备自身的一个蓝牙设备适配器,整个系统只有一个蓝牙适配器。经过它能够发现其余蓝牙设备,查询绑定(配对)设备的列表,使用已知的 Mac 地址实例化 BluetoothDevice
以及建立 BluetoothServerSocket
用来侦听来自其余设备的通讯。并发
BluetoothDevice框架
表示远程的蓝牙设备。利用它能够经过 BluetoothSocket
请求与某个远程设备创建链接,或查询有关该设备的信息,例如设备的名称、地址、类和绑定状态等。异步
BluetoothSocketsocket
表示蓝牙套接字接口(与 TCP Socket 类似)。这是容许应用经过 InputStream
和 OutputStream
与其余蓝牙设备交换数据的节点。正是利用这个对象来完成蓝牙设备间的数据交换,ui
BluetoothServerSocket
表示用于侦听传入请求的开发服务器套接字(相似于 TCP ServerSocket)要链接两台 Android 设备,其中一台设备必须使用此类开发的一个服务器套接字。当一台远程蓝牙设备向此设备发出链接请求时,BluetoothServerSocket
将会在接受链接后返回已链接的 BluethoothSocket
。
BluetoothClass
描述蓝牙设备的通常特性和功能。这是一组只读属性,用于定义设备的主要和次要设备类及其服务。不过,它不能可靠地描述设备支持的全部蓝牙配置文件和服务,而是适合做为设备类型提示。
BluetoothProfile
表示蓝牙配置文件的接口。蓝牙配置文件是适用于设备间蓝牙通讯的无线接口规范。免提配置文件即是一个示例。如需了解关于配置文件的详细讨论,参考下面配置文件的讲解
BluetoothHeadset
提供蓝牙耳机支持,以便与手机配合使用。其中包括蓝牙耳机和免提(1.5版)配置文件。BluetoothProfile 的实现类
BlutoothA2dp
定义高质量音频如何经过蓝牙链接和流式传输,从一台设备传输到另外一台设备。“A2DP”表明高级音频分发配置文件。是 BluetoothProfile 的实现类
BluetoothHealth
表示用于控制蓝牙服务的健康设备配置文件代理。 BluetoothProfile 的实现类。
BluetoothGatt
BluetoothProfile 的实现类。与低功耗蓝牙通讯有关的配置文件代理
BluetoothHealthCallback
用于实现 BluetoothHealth 回调的抽象类。必须扩展此类并实现回调方法,以接收关于应用注册状态和蓝牙通道状态变化的更新内容。
BluetoothHealthAppConfiguration
表示第三方蓝牙健康应用注册的应用配置,以便与远程蓝牙健康设备通讯
BluetoothProfile.ServiceListener
在 BluetoothProfile IPC 客户端链接到服务(即,运行特定配置文件的内部服务)或断开服务链接时向其发送通知的接口。
使用蓝牙必须声明权限 BLUETOOTH 才能够执行蓝牙通讯。
<mainifest> <uses-permission android:name = "android.permission.BLUETOOTH"/> <!--启用应用启动设备发现或者操做蓝牙设备的超级管理员--> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> </mainifest>
getDefaultAdapter()
方法。会返回一个表示设备自身的蓝牙适配器(蓝牙无线装置)的 BluetoothAdapter。getDefaultAdapter()
返回 null 则说明该设备不支持蓝牙。BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if(mBluetoothAdapter == null){ // 说明此设备不支持蓝牙操做 }
isEnabled()
。返回 false 则说明蓝牙处于关闭状态。请求启用蓝牙。使用 ACTION_REQUEST_ENABLE
操做 Intent 调用 startActivityForResult()
将经过系统设置发出启用蓝牙的请求。也能够经过 mBluetoothAdapter.enable()
直接打开蓝牙。// 没有开始蓝牙 if(!mBluetoothAdapter.isEnabled()){ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent,REQUEST_ENBLE_BT); }
咱们的应用也能够选择侦听 ACTION_STATE_CHANGED
广播 Intent。每当蓝牙状态发生变化时,系统会广播此 Intent。此广播包含额外字段 EXTRA_STATE
和 EXTRA_PREVIOUS_STATE
分别表示新的和旧的蓝牙状态。
使用 BluetoothAdapter 能够经过设备发现或经过查询配对设备的列表来查找远程蓝牙设备。
设备发现是一个扫描过程,它会搜索局部区域内已启用蓝牙功能的设备,而后请求一些关于各台设备的信息。这个过程也称为发现、查询、扫描。局部区域内的蓝牙设备仅在其当前已启用可检测性时才会响应发现请求。若是设备能够检测到,它将经过共享一些信息(例如设备名称、类及其惟一MAC地址)来响应发现请求。利用此信息,执行发现的设备能够选择发起到被发现设备的链接。
在首次与远程设备创建链接后,将会自动向用户显示配对请求。设备完成配对后,将会保存关于该设备的基本信息(如 设备名称、MAC 地址)。而且可使用 Bluetooth API 读取这些信息。利用远程设备的已知 Mac 地址能够随时向其发起链接,而不需执行发现操做(假定该设备处于有效范围内)。
被配对和被链接之间存在差异。被配对意味着两台设备知晓彼此的存在,具备可用于身份验证的共享链路密钥,而且可以与彼此创建加密链接。被链接意味着设备当前共享一个 RFCOMM 通道,而且可以向彼此传输数据。当前的 Android Bluetooth API 要求对设备进行配对,而后才能创建 RFCOMM 链接(在使用 Bluetooth API 发起加密链接时,会自动执行配对)。Android 设备是默认处于不可检测状态的。
在执行设备发现以前,有必要查询已配对的设备集合。用来了解设备是否处于已知状态。经过 getBondedDevices()
来实现,这将返回表示已配对设备的一组 BluetoothDevice
。
例如:咱们能够查询全部已配对的设备,而后使用 ArrayAdapter 向用户显示每台设备的名称:
Set<BluetoothDevice> pairedDevices = mBlutooothAdapter.getBondedDevices(); if(pairedDevices.size() > 0){ for(BluetoothDevice device:pairedDevices){ // 把名字和地址取出来添加到适配器中 mArrayAdapter.add(device.getName()+"\n"+ device.getAddress()); } }
要发起链接仅须要知道目标蓝牙设备的 Mac 地址就能够了。
发现设备使用 startDiscovery()
该进程为异步进程。该方法会马上返回一个布尔值,指示是否已成功启动发现操做。发现进程一般包含约 12 秒的查询扫描,以后对发现的设备进行扫描,以检索其蓝牙设备的名字。
咱们的应用必须针对 ACTION_FOUND
Intent 注册一个 BroadcastReceiver,以便接受每台发现的设备的信息。针对每台设备,系统会广播 ACTION_FOUND Intent。这个 Intent 会携带额外的字段 EXTRA_DEVICE 和 EXTRA_CLASS。这二者分别包含 BluetoothDevice 和 BluetoothClass。
// 建立一个接受 ACTION_FOUND 的 BroadcastReceiver private final BroadcastReceiver mReceiver = new BroadcastReceiver(){ public void onReceive(Context context,Intent intent){ String action = intent.getAction(); // 当 Discovery 发现了一个设备 if(BluetoothDevice.ACTION_FOUND.equals(action)){ // 从 Intent 中获取发现的 BluetoothDevice BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 将名字和地址放入要显示的适配器中 mArrayAdapter.add(device.getName + "\n" + device.getAddress()); } } }; // 注册这个 BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReiver,filter); // 在 onDestroy 中 unRegister
注意 执行 discovery 对于蓝牙适配器来讲是一个很是繁重的过程,而且会消耗大量资源。在找到要链接的设备后,要确保使用 cancelDiscovery()
来中止发现,而后尝试链接。若是您已经和某台设备进行链接,那么这个时候执行发现操做会大幅度的减小此链接可用的带宽!所以不该该在处于链接状态的时候执行发现操做!
若是咱们但愿咱们的设备是能够被其余设备检测到的,可使用 ACTION_REQUEST_DISCOVERABLE
来操做 Intent 调用 startActivityForResult(Intent,int)
。这样会经过系统设置发出启用可检测到模式的请求(无需中止咱们的应用)。默认状况下,设备会变为可检测状态而且持续 120 秒钟。咱们还能够经过 EXTRA_DISCOVERABLE_DURATION
Intent Extra
来定义不一样的持续时间。能够设置的最大持续时间为 3600 秒。值为 0 表示始终能够被检测到。任何小于 0 或者大于 3600 的值都会自动设置为 120 秒钟。
例如:
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300); startActivityForResult(discoverableIntent);
将显示对话框,请求用户容许将设备设为可检测到。若是用户响应为 YES,则设备将变为可检测到并持续指定的时间量。而后您的 Activity 将会收到对 onActivityResult() 回调的调用,其结果代码等于设备可检测到的持续时间。若是用户响应 NO 或者出现错误,结果代码为 RESULT_CANCELED
若是设备没有打开蓝牙,则启用设备可检测性的时候会自动启用蓝牙。
设备将在分配的时间内以静默方式保持可检测到模式。若是咱们但愿在可检测到模式发生变化时收到通知,能够利用 ACTION_SCAN_MODE_CHANGED
Intent 注册 BroadcastReceiver。它将包含额外字段 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE,二者分别告诉咱们新的和旧的扫描模式。每一个字段可能包括SCAN_MODE_CONNECTABLE_DISCOVERABLE(可检测到模式)、SCAN_MODE_CONNECTABLE(未处于可检测模式但能够接受链接)、SCAN_MODE_NOE(未处于可检测到模式而且没法链接)
要在两台设备上的应用之间建立链接,必须同时实现服务端和客户端机制,由于其中一台设备必须开放服务器套接字,而另外一台设备必须发起链接(使用服务器设备的 MAC 地址发起链接)。当服务器和客户端在同一 RFCOMM 通道上分别拥有已链接的 BluetoothSocket 时,两者将被视为彼此链接。在这种状况下每台设备都能得到输入和输出流式传输,而且能够开始传输数据。
服务端和客户端分别以不一样的方式来得到 BluetoothSocket 。服务器将在传入链接被接受时收到套接字。客户端将在其打开到服务器的 RFCOMM 通道时收到该套接字。
一种实现方式是自动将每台设备准备为一个服务器,从而使每台设备开发一个服务器套接字并侦听链接。而后任一设备能够发起与另外一台设备的链接,并成为客户端。或者其中一台设备可显示“托管”链接并按需开放一个服务器套接字,从而另外一台设备则直接发起链接。
在链接以前若是两个设备没有配对,则系统会自动发出配对请求
当链接两台设备时,其中一台必须保持开发的 BluetoothServerSocket
来充当服务器,用于监听传入的链接请求,在接受了请求后提供一个已经链接的 BluetoothSocket
。从 BluetoothServerSocket
链接获取 BluetoothSocket
后就能够调用 close 来关闭这个等待了。
通用惟一标识符(UUID),用于表示惟一标识信息的字符串ID,128位。可使用网络上众多的随机 UUID 生成器,而后经过 formString(String)
来初始化一个 UUID。
服务器套接字接受链接的基本过程
经过 listenUsingRfcommWithServiceRecord(String,UUID)获取 BluetoothServerSocket
字符串是咱们本身定义的服务的可识别名称,能够直接使用包名。系统会自定将其写入到设备上的新服务发现协议(SDP)数据库条目中。UUID 也在 SDP 中,做为与客户端设备链接协议的匹配规则。只有客户端和这里的UUID 同样了才能够会被链接
accept()
侦听链接请求
阻塞调用,将在链接被接受或者发生异常的时候返回,操做成功后,会返回 BluetoothSocket
。
除非要接受更多的链接,不然调用 close()
来关闭这个监听
这样会释放服务器套接字及其全部资源,但不会关闭已经链接的 BluetoothSocket。与 TCP/IP 不一样的是,RFCOMM 一次只容许每一个通道有一个已经链接的客户端。
放在子线程中去执行。
例子:
private class AcceptThread extend Thread{ private final BluetoothServerSocket mServerSocket; public AcceptThread(){ mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID); } public void run(){ BluetoothSocket socket = null; while(true){ socket = mServerSocket.accept(); if(socket!=null){ // 自定义方法 manageConnectedSocket(socket); mServerSocket.close(); break; } } } public void cancle(){ mServerSocket.close(); } }
要想和保持开发服务器套接字的设备创建链接,必须首先要获取该设备的 BluetoothDevice 对象。而后使用这个对象来获取 BluetoothSocket 并发起链接。
客户端链接的基本过程
经过 BluetoothDevice 的 createRfcommSocketToServiceRecord(UUID) 获取 BluetoothSocket 对象
这里的 UUID 要和服务器端的一致
经过 connect() 发起链接
执行此方法后,系统将会在远程设备上执行 SDP 查找,来匹配 UUID。若是查找成功了而且远程设备接受了该链接,它将共享 RFCOMM 通道在链接期间使用。这个时候 connect() 就会返回。这个方法也是阻塞的,若是失败或者超时(12秒以后),将引起异常。
调用 connect() 的时候要确保客户端没有执行发现操做。若是执行了会大幅度下降链接的速度,增长失败的可能性
例子
private class ConnectThread extend Thread{ private BluetoothDevice mDevice; private BluetoothSocket mSocket; public ConnectThread(BluetoothSocket device){ mDevice = device; // 这里的 UUID 须要和服务器的一致 mSocket = device.createRfcommSocketToServiceRecord(My_UUID); } public void run(){ // 关闭发现设备 mBluetoothAdapter.cancelDiscovery(); try{ mSocket.connect(); }catch(IOException connectException){ try{ mSocket.close(); }catch(IOException closeException){ return; } } // 自定义方法 manageConnectedSocket(mmSocket); } public void cancle(){ try{ mSocket.close(); }cathc(IOException closeException){ } } }
在链接以前调用 cancleDiscovery()
在进行链接以前应该始终调用这个方法,并且调用的时候无需检测是否正在扫描。
创建链接后的两个设备都有一个 BluetoothSocket
经过这个 Socket 就能够在这两个设备间传输数据了。
过程:
从 Android 3.0 开始, Bluetooth API 便支持使用蓝牙配置文件。蓝牙配置文件是适用于设备间蓝牙通讯的无线接口规范。
蓝牙配置文件就是设备间通讯(蓝牙设备)的一种规范
免提配置文件即是一个示例,对于链接到无线耳机的手机,两台设备都必须支持免提配置文件。咱们也能够经过实现接口 BluetoothProfile
来写入本身的类来支持特定的蓝牙配置文件。Android API 提供了如下的几种蓝牙配置文件的实现:
使用配置文件的基本步骤
getProfileProxy()
,创建到配置文件所关联的配置文件代理对象的链接。BluetoothProfile.ServiceListener
。这个监听会在客户端链接到服务或者断开服务链接的时候发送通知。onServiceConnected()
中获取配置文件代理对象的句柄。例子: 如何链接到 BluetoothHeadset 代理对象,以便可以控制耳机配置文件:
BluetoothHeadset mBluetoothHeadset; // 获取默认蓝牙适配器 BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 设置监听(监听链接状态) private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener(){ public void onServiceConnected(int profile,BluetoothProfile proxy){ if(profile == BluetoothProfile.HEADSET){ mBluetoothHeadset = (BluetoothHeadset) } } public void onServiceDisconnected(int profile){ if(profile == BluetoothProfile.HEADSET){ mBluetoothHeadset = null; } } } // 创建与配置文件代理的链接 mBluetoothAdapter.getProfileProxy(contenxt,mProfileListener,BluetoothProfile.HEADSET); // 使用 mBluetoothHeadset 代理内部的方法 // 使用完毕后关闭 mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);
从 Android 3.0 开始。应用能够注册接受耳机所发送的预约义的供应商特定 AT 命令的系统广播(例如 Plantronics +XEVENT命令)(也就是说咱们的应用能够接受耳机蓝牙商预约义的命令)。如:应用能够接受指示所链接设备的电池电量的广播,并根据须要通知用户或采起其余操做。使用 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent 建立广播接收器,用来处理耳机供应商特定的 AT 命令。
Android 4.0 引入了对蓝牙健康设备配置文件(HDP)的支持。这可使用咱们的应用使用蓝牙与支持蓝牙功能的健康设备进行通讯(心率检测仪、血糖仪、温度计、台秤)
建立 HDP 应用:
获取 BluetoothHealth 代理对象
与常规耳机和 A2DP 相似。使用 ServiceListener 和 HEALTH 配置文件类型参数来调用 getProfileProxy(),来和配置文件代理对象创建链接。
建立 BluetoothHealthCallback 并注册充当健康聚集设备的应用配置(BluetoothHealthAPPConfiguration)
创建到健康设备的链接
成功链接到健康设备后,使用文件描述符对健康设备执行读写操做
完成后,关闭健康通道并取消注册该应用,该通道长时间闲置也会关闭。
关于普通蓝牙设备和普通蓝牙设备之间的链接通讯
关于蓝牙设备和蓝牙仪器(蓝牙耳机、电子秤等等相似产品)
这种之间的通讯是经过配置文件代理来实现的。
都有一个对应的配置文件代理类。具体的操做是经过这个对象来完成。