Android 蓝牙开发详解

Android平台支持蓝牙网络协议栈,实现蓝牙设备之间数据的无线传输。本文档描述了怎样利用android平台提供的蓝牙

API去实现蓝压设备之间的通信。蓝牙具有point-to-point 和 multipoint两种连接功能。 使用蓝牙API,可以做到: * 搜索蓝

牙设备 * 从本地的Bluetooth adapter中查询已经配对的设备 * 建立RFCOMM通道 * 通过service discovery连接到其它设备

* 在设备之间传输数据 * 管理多个连接

基础知识

本文档介绍了如何使用Android的蓝牙API来完成的四个必要的主要任务,使用蓝牙进行设备通信,主要包含四个部分:蓝

牙设置、搜索设备(配对的或可见的)、连接、传输数据。 所有的蓝牙API在android.bluetooth包中。实现这些功能主要

需要下面这⼏个类和接⼝:

BluetoothAdapter 代表本地蓝牙适配器(蓝牙发射器),是所有蓝牙交互的⼊⼝。通过它可以搜索其它蓝牙设备,查询已

经配对的设备列表,通过已知的MAC地址创建BluetoothDevice,创建BluetoothServerSocket监听来自其它设备的通信。

BluetoothDevice 代表了⼀个远端的蓝牙设备, 使用它请求远端蓝牙设备连接或者获取 远端蓝牙设备的名称、地址、种类

和绑定状态。 (其信息是封装在 bluetoothsocket 中) 。

BluetoothSocket 代表了⼀个蓝牙套接字的接⼝(类似于 tcp 中的套接字) ,他是应用程 序通过输⼊、输出流与其他蓝牙

设备通信的连接点。

BluetoothServerSocket 代表打开服务连接来监听可能到来的连接请求 (属于 server 端) , 为了连接两个蓝牙设备必须

有⼀个设备作为服务器打开⼀个服务套接字。 当远端设备发起连 接连接请求的时候,并且已经连接到了的时

候,Blueboothserversocket 类将会返回⼀个 bluetoothsocket。

BluetoothClass 描述了⼀个设备的特性(profile)或该设备上的蓝牙⼤致可以提供哪些服务(service),但不可信。比如,

设备是⼀个电话、计算机或手持设备;设备可以提供audio/telephony服务等。可以用它来进行⼀些UI上的提示。

BluetoothProfile

BluetoothHeadset 提供手机使用蓝牙耳机的支持。这既包括蓝牙耳机和免提(V1.5)模式。

BluetoothA2dp 定义高品质的音频,可以从⼀个设备传输到另⼀个蓝牙连接。 “A2DP的”代表高级音频分配模式。

BluetoothHealth 代表了医疗设备配置代理控制的蓝牙服务

BluetoothHealthCallback ⼀个抽象类,使用实现BluetoothHealth回调。你必须扩展这个类并实现回调方法接收更新应用

程序的注册状态和蓝牙通道状态的变化。

BluetoothHealthAppConfiguration 代表⼀个应用程序的配置,蓝牙医疗第三方应用注册与远程蓝牙医疗设备交流。

BluetoothProfile.ServiceListener 当他们已经连接到或从服务断开时通知BluetoothProfile IPX的客户时⼀个接⼝(即运行

⼀个特定的配置文件,内部服务)。

蓝牙权限

为了在你的应用中使用蓝牙功能,至少要在AndroidManifest.xml中声明两个权限:BLUETOOTH(任何蓝牙相关API都要

使用这个权限) 和 BLUETOOTH_ADMIN(设备搜索、蓝牙设置等)。

为了执行蓝牙通信,例如连接请求,接收连接和传送数据都必须有BLUETOOTH权限。

必须要求BLUETOOTH_ADMIN的权限来启动设备发现或操纵蓝牙设置。⼤多数应用程序都需要这个权限能⼒,发现当地

的蓝牙设备。此权限授予其他的能⼒不应该使用,除非应用程序是⼀个“电源管理”,将根据用户要求修改的蓝牙设置

注释:要请求BLUETOOTH_ADMIN的话,必须要先有BLUETOOTH。

在你的应用manifest 文件中声明蓝牙权限。例如:


...

通过查看资料来声明应用权限获取更多的信息。

蓝牙设置

在你的应用通过蓝牙进行通信之前,你需要确认设备是否支持蓝牙,如果支持,确信它被打开。

如果不支持,则不能使用蓝牙功能。如果支持蓝牙,但不能够使用,你刚要在你的应用中请求使用蓝牙。这个要两步完

成,使用BluetoothAdapter。

1.获取BluetoothAdapter

所有的蓝牙活动请求BluetoothAdapter,为了获取BluetoothAdapter,呼叫静态方法getDefaultAdapter() 。这个会返回⼀

个BluetoothAdapter,代表设备自⼰的蓝牙适配器(蓝牙无线电)。这个蓝牙适配器应用于整个系统中,你的应用可以通

过这个对象进行交互。如果getDefaultAdapter()返回null,则这个设备不支持蓝牙。例如:


BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter == null) {

// Device does not support Bluetooth

}

2.打开蓝牙

其次。你需要确定蓝牙能够使用。通过isEnabled()来检查蓝牙当前是否可用。如果这个方法返回false,则蓝牙不能够使

用。为了请求蓝牙使用,呼叫startActivityForResult()与的ACTION_REQUEST_ENABLE动作意图。通过系统设置中启

用蓝牙将发出⼀个请求(不停止蓝牙应用)。例如:


if (mBluetoothAdapter.isEnabled()) {

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

![http://developer.android.com/images/bt_enable_request.png]

对话框中显示请求使用蓝牙权限。如果响应"Yes",这个进程完成(或失败)后你的应用将能够使用蓝牙。

REQUEST_ENABLE_BT常量作为⼀个整型传到startActivityForResult()中(值必须⼤于0),该系统传回给你,在你

onActivityResult()作为实现的requestCode参数。

如果调用蓝牙成功,你的Activity就会在onActivityResult()中收到RESULT_OK结果,如果蓝牙不能使用由于错误(或用户

响应“NO”那么结果返回RESULT_CANCELED。

除了通过onActivityResult(),还可以通过监听ACTION_STATE_CHANGED这个broadcast Intent来知道蓝牙状态是否改

变。这个Intent包含EXTRA_STATE,EXTRA_PREVIOUS_STATE两个字段,分别代表新旧状态。可能的值是

STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, 还有STATE_OFF。

⼩贴: Enabling discoverability 将自动启用蓝牙。如果您计划执行蓝牙活动之前,始终使设备可发现,你可以跳过上面的

步骤2。参阅enabling discoverability。

搜索设备

使用BluetoothAdapter可以通过设备搜索或查询配对设备找到远程Bluetooth设备。

Device discovery(设备搜索)是⼀个扫描搜索本地已使能Bluetooth设备并且从搜索到的设备请求⼀些信息的过程(有时

候会收到类似“discovering”,“inquiring”或“scanning”)。但是,搜索到的本地Bluetooth设备只有在打开被发现功能后才

会响应⼀个discovery请求,响应的信息包括设备名,类,唯⼀的MAC地址。发起搜寻的设备可以使用这些信息来初始化

跟被发现的设备的连接。 ⼀旦与远程设备的第⼀次连接被建立,⼀个pairing请求就会自动提交给用户。如果设备已配对,

配对设备的基本信息(名称,类,MAC地址)就被保存下来了,能够使用Bluetooth API来读取这些信息。使用已知的远程

设备的MAC地址,连接可以在任何时候初始化而不必先完成搜索(当然这是假设远程设备是在可连接的空间范围内)。

需要记住,配对和连接是两个不同的概念:

配对意思是两个设备相互意识到对方的存在,共享⼀个用来鉴别身份的链路键(link-key),能够与对方建立⼀个加密的连

接。

连接意思是两个设备现在共享⼀个RFCOMM信道,能够相互传输数据。

目前Android Bluetooth API's要求设备在建立RFCOMM信道前必须配对(配对是在使用Bluetooth API初始化⼀个加密连接

时自动完成的)。

下面描述如何查询已配对设备,搜索新设备。

注意:Android的电源设备默认是不能被发现的。用户可以通过系统设置让它在有限的时间内可以被发现,或者可以在应

用程序中要求用户使能被发现功能。

查找匹配设备

在搜索设备前,查询配对设备看需要的设备是否已经是已经存在是很值得的,可以调用getBondedDevices()来做到,该函

数会返回⼀个描述配对设备BluetoothDevice的结果集。例如,可以使用ArrayAdapter查询所有配对设备然后显示所有设备

名给用户:

Set pairedDevices = mBluetoothAdapter.getBondedDevices();

// If there are paired devices

if (pairedDevices.size() > 0) {

// Loop through paired devices

for (BluetoothDevice device : pairedDevices) {


// Add the name and address to an array adapter to show in a ListView

mArrayAdapter.add(device.getName() + "\n" + device.getAddress());

}

};

BluetoothDevice对象中需要用来初始化⼀个连接唯⼀需要用到的信息就是MAC地址。

扫描设备

要开始搜索设备,只需简单的调用startDiscovery() 。该函数时异步的,调用后立即返回,返回值表示搜索是否成功开始。

搜索处理通常包括⼀个12秒钟的查询扫描,然后跟随⼀个页面显示搜索到设备Bluetooth名称。

应用中可以注册⼀个带ACTION_FOUND Intent的BroadcastReceiver,搜索到每⼀个设备时都接收到消息。对于每⼀个设

备,系统都会广播ACTION_FOUND Intent,该Intent携带着而外的字段信息EXTRA_DEVICE和EXTRA_CLASS,分别包

含⼀个BluetoothDevice和⼀个BluetoothClass。

下面的示例显示如何注册和处理设备被发现后发出的广播:

代码如下:

// Create a BroadcastReceiver for ACTION_FOUND

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

// When discovery finds a device

if (BluetoothDevice.ACTION_FOUND.equals(action)) {

// Get the BluetoothDevice object from the Intent

BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

// Add the name and address to an array adapter to show in a ListView

mArrayAdapter.add(device.getName() + "\n" + device.getAddress());

}

}

};

// Register the BroadcastReceiver

IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);

registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

警告:完成设备搜索对于Bluetooth适配器来说是⼀个重量级的处理,要消耗⼤量它的资源。⼀旦你已经找到⼀个设备来连

接,请确保你在尝试连接前使用了cancelDiscovery()来停止搜索。同样,如果已经保持了⼀个连接的时候,同时执行搜索

设备将会显著的降低连接的带宽,所以在连接的时候不应该执行搜索发现。

使能被发现

如果想让本地设备被其他设备发现,可以带ACTION_REQUEST_DISCOVERABLE action Intent调用

startActivityForResult(Intent, int) 方法。该方法会提交⼀个请求通过系统刚设置使设备出于可以被发现的模式(而不影响

应用程序)。默认情况下,设备在120秒后变为可以被发现的。可以通过额外增加EXTRA_DISCOVERABLE_DURATION

Intent自定义⼀个值,最⼤值是3600秒,0表示设备总是可以被发现的(⼩于0或者⼤于3600则会被自动设置为120秒)。

下面示例设置时间为300:

Intent discoverableIntent = new

Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

startActivity(discoverableIntent);

! [http://developer.android.com/images/bt_enable_discoverable.png]

询问用户是否允许打开设备可以被发现功能时会显示⼀个对话框。如果用户选择“Yes”,设备会在指定时间过后变为可以被

发现的。Activity的onActivityResult()回调函数被调用,结果码等于设备变为可以被发现所需时长。如果用户选择“No”或者

有错误发生,结果码会是Activity.RESULT_CANCELLED。

提示:如果Bluetooth没有启用,启用Bluetooth可被发现功能能够自动开启Bluetooth。

在规定的时间内,设备会静静的保持可以被发现模式。如果想在可以被发现模式被更改时受到通知,可以用

ACTION_SCAN_MODE_CHANGED Intent注册⼀个BroadcastReceiver,包含额外的字段信息EXTRA_SCAN_MODE和

EXTRA_PREVIOUS_SCAN_MODE分别表示新旧扫描模式,其可能的值为

SCAN_MODE_CONNECTABLE_DISCOVERABLE(discoverable mode),SCAN_MODE_CONNECTABLE(not in

discoverable mode but still able to receive connections),SCAN_MODE_NONE(not in discoverable mode and

unable to receive connections)。 如果只需要连接远程设备就不需要打开设备的可以被发现功能。只在应用作为⼀个服

务器socket的宿主用来接收进来的连接时才需要使能可以被发现功能,因为远程设备在初始化连接前必须先发现了你的设

备。

连接设备


为了在两台设备上创建⼀个连接,你必须在软件上实现服务器端和客户端的机制,因为⼀个设备必须必须打开⼀个server

socket,而另⼀个必须初始化这个连接(使用服务器端设备的MAC地址进行初始化)。 当服务器端和客户端在同⼀个

RFCOMM信道上都有⼀个BluetoothSocket时,就可以认为它们之间建立了⼀个连接。在这个时刻,每个设备能获得⼀个

输出流和⼀个输⼊流,也能够开始数据传输。本节介绍如何在两个设备之间初始化⼀个连接。 服务器端和客户端获得

BluetoothSocket的方法是不同的,服务器端是当⼀个进⼊的连接被接受时才产生⼀个BluetoothSocket,客户端是在打开

⼀个到服务器端的RFCOMM信道时获得BluetoothSocket的。

! [http://developer.android.com/images/bt_pairing_request.png]

⼀种实现技术是,每⼀个设备都自动作为⼀个服务器,所以每个设备都有⼀个server socket并监听连接。然后每个设备都

能作为客户端建立⼀个到另⼀台设备的连接。另外⼀种代替方法是,⼀个设备按需打开⼀个server socket,另外⼀个设备

仅初始化⼀个到这个设备的连接。

Note: 如果两个设备在建立连接之前并没有配对,那么在建立连接的过程中,Android框架将自动显示⼀个配对请求的

notification或者⼀个对话框,如Figure 3所示。所以,在尝试连接设备时,你的应用程序无需确保设备之间已经进行了配

对。你的RFCOMM连接将会在用户确认配对之后继续进行,或者用户拒绝或者超时之后失败。

作为服务器连接

如果要连接两个设备,其中⼀个必须充当服务器,通过持有⼀个打开的BluetoothServerSocket对象。服务器socket的作用

是侦听进来的连接,如果⼀个连接被接受,提供⼀个连接好的BluetoothSocket对象。从BluetoothServerSocket获取

到BluetoothSocket对象之后,BluetoothServerSocket就可以(也应该)丢弃了,除非你还要用它来接收更多的连接。

下面是建立服务器socket和接收⼀个连接的基本步骤:

1.通过调用listenUsingRfcommWithServiceRecord(String, UUID)得到⼀个BluetoothServerSocket对象。

该字符串为服务的识别名称,系统将自动写⼊到⼀个新的服务发现协议(SDP)数据库接⼊⼝到设备上的(名字是任意

的,可以简单地是应用程序的名称)项。 UUID也包括在SDP接⼊⼝中,将是客户端设备连接协议的基础。也就是说,当

客户端试图连接本设备,它将携带⼀个UUID用来唯⼀标识它要连接的服务,UUID必须匹配,连接才会被接受。

2.通过调用accept()来侦听连接请求。

这是⼀个阻塞的调用,知道有连接进来或者产生异常才会返回。只有远程设备发送⼀个连接请求,并且携带的UUID与侦听

它socket注册的UUID匹配,连接请求才会被接受。如果成功,accept()将返回⼀个连接好的BluetoothSocket对象。

3.除非需要再接收另外的连接,否则的话调用close() 。

close()释放server socket和它的资源,但不会关闭连接accept()返回的连接好的BluetoothSocket对象。与TCP/IP不

同,RFCOMM同⼀时刻⼀个信道只允许⼀个客户端连接,因此⼤多数情况下意味着在BluetoothServerSocket接受⼀个连

接请求后应该立即调用close()。

accept()调用不应该在主Activity UI线程中进行,因为这是个阻塞的调用,会妨碍其他的交互。经常是在在⼀个新线程中做

BluetoothServerSocket或BluetoothSocket的所有⼯作来避免UI线程阻塞。注意所有BluetoothServerSocket或

BluetoothSocket的方法都是线程安全的。

示例:

下面是⼀个简单的接受连接的服务器组件代码示例:

示例

private class AcceptThread extends Thread {

private final BluetoothServerSocket mmServerSocket;

public AcceptThread() {

// Use a temporary object that is later assigned to mmServerSocket,

// because mmServerSocket is final

BluetoothServerSocket tmp = null;

try {

// MY_UUID is the app's UUID string, also used by the client code

tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

} catch (IOException e) { }

mmServerSocket = tmp;

}

public void run() {

BluetoothSocket socket = null;

// Keep listening until exception occurs or a socket is returned

while (true) {

try {

socket = mmServerSocket.accept();

} catch (IOException e) {

break;

}

// If a connection was accepted

if (socket != null) {

// Do work to manage the connection (in a separate thread)

manageConnectedSocket(socket);

mmServerSocket.close();

break;

}

}

}

/* * Will cancel the listening socket, and cause the thread to finish * /

public void cancel() {

try {

mmServerSocket.close();

} catch (IOException e) { }

}

}

本例中,仅仅只接受⼀个进来的连接,⼀旦连接被接受获取到BluetoothSocket,就发送获取到的BluetoothSocket给⼀个

单独的线程,然后关闭BluetoothServerSocket并跳出循环。

注意:accept()返回BluetoothSocket后,socket已经连接了,所以在客户端不应该呼叫connnect()。

manageConnectedSocket()是⼀个虚方法,用来初始化线程好传输数据。

通常应该在处理完侦听到的连接后立即关闭BluetoothServerSocket。在本例中,close()在得到BluetoothSocket后马上被

调用。还需要在线程中提供⼀个公共的方法来关闭私有的BluetoothSocket,停止服务端socket的侦听。

作为客户端连接

为了实现与远程设备的连接,你必须首先获得⼀个代表远程设备BluetoothDevice对象。然后使用BluetoothDevice对象来

获取⼀个BluetoothSocket来实现来接。

下面是基本的步骤:

1.用BluetoothDevice调用createRfcommSocketToServiceRecord(UUID)获取⼀个BluetoothSocket对象。 这个初始化的

BluetoothSocket会连接到BluetoothDevice。UUID必须匹配服务器设备在打开BluetoothServerSocket 时用到的

UUID(用java.util.UUID) listenUsingRfcommWithServiceRecord(String, UUID)。可以简单的生成⼀个UUID串然后在服务

器和客户端都使用该UUID。

2.调用connect()完成连接 当调用这个方法的时候,系统会在远程设备上完成⼀个SDP查找来匹配UUID。如果查找成功并

且远程设备接受连接,就共享RFCOMM信道,connect()会返回。这也是⼀个阻塞的调用,不管连接失败还是超时(12

秒)都会抛出异常。

注意:要确保在调用connect()时没有同时做设备查找,如果在查找设备,该连接尝试会显著的变慢,慢得类似失败了。

实例: 下面是⼀个完成Bluetooth连接的样例线程:

private class ConnectThread extends Thread {

private final BluetoothSocket mmSocket;

private final BluetoothDevice mmDevice;

public ConnectThread(BluetoothDevice device) {

// Use a temporary object that is later assigned to mmSocket,

// because mmSocket is final

BluetoothSocket tmp = null;

mmDevice = device;

// Get a BluetoothSocket to connect with the given BluetoothDevice

try {

// MY_UUID is the app's UUID string, also used by the server code

tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

} catch (IOException e) { }

mmSocket = tmp;

}

public void run() {

// Cancel discovery because it will slow down the connection

mBluetoothAdapter.cancelDiscovery();

try {

// Connect the device through the socket. This will block

// until it succeeds or throws an exception

mmSocket.connect();

} catch (IOException connectException) {

// Unable to connect; close the socket and get out

try {

mmSocket.close();

} catch (IOException closeException) { }

return;

}

// Do work to manage the connection (in a separate thread)

manageConnectedSocket(mmSocket);

}

/* * Will cancel an in-progress connection, and close the socket * /

public void cancel() {

try {

mmSocket.close();

} catch (IOException e) { }

}

}

注意 : 到cancelDiscovery()在连接操作前被调用。在连接之前,不管搜索有没有进行,该调用都是安全的,不需要确认

(当然如果有要确认的需求,可以调用isDiscovering() )。 manageConnectedSocket()是⼀个虚方法,用来初始化线程

好传输数据。 在对BluetoothSocket的处理完成后,记得调用close()来关闭连接的socket和清理所有的内部资源。

管理连接

如果已经连接了两个设备,他们都已经拥有各自的连接好的BluetoothSocket对象。那就是⼀个有趣的开始,因为你可以在

设备间共享数据了。使用BluetoothSocket,传输任何数据通常来说都很容易了:

1.通过socket获取输⼊输出流来处理传输(分别使用getInputStream()和getOutputStream() )。

2.用read(byte[])和write(byte[])来实现读写。

仅此而已。

当然,还是有很多细节需要考虑的。首要的,需要用⼀个专门的线程来实现流的读写。只是很重要的,因为read(byte[])和

write(byte[])都是阻塞的调用。read(byte[])会阻塞直到流中有数据可读。write(byte[])通常不会阻塞,但是如果远程设备调

用read(byte[])不够快导致中间缓冲区满,它也可能阻塞。所以线程中的主循环应该用于读取InputStream。线程中也应该

有单独的方法用来完成写OutputStream。

示例

下面是⼀个如上面描述那样的例⼦:

private class ConnectedThread extends Thread {

private final BluetoothSocket mmSocket;

private final InputStream mmInStream;

private final OutputStream mmOutStream;

public ConnectedThread(BluetoothSocket socket) {

mmSocket = socket;

InputStream tmpIn = null;

OutputStream tmpOut = null;

// Get the input and output streams, using temp objects because

// member streams are final

try {

tmpIn = socket.getInputStream();

tmpOut = socket.getOutputStream();

} catch (IOException e) { }

mmInStream = tmpIn;

mmOutStream = tmpOut;

}

public void run() {

byte[] buffer = new byte[1024]; // buffer store for the stream

int bytes; // bytes returned from read()

// Keep listening to the InputStream until an exception occurs

while (true) {

try {

// Read from the InputStream

bytes = mmInStream.read(buffer);

// Send the obtained bytes to the UI activity

mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)

.sendToTarget();

} catch (IOException e) {

break;

}

}

}

/* Call this from the main activity to send data to the remote device * /

public void write(byte[] bytes) {

try {

mmOutStream.write(bytes);

} catch (IOException e) { }

}

/* Call this from the main activity to shutdown the connection * /

public void cancel() {

try {

mmSocket.close();

} catch (IOException e) { }

}

}

构造函数中得到需要的流,⼀旦执行,线程会等待从InputStream来的数据。当read(byte[])返回从流中读到的字节后,数

据通过父类的成员Handler被送到主Activity,然后继续等待读取流中的数据。 向外发送数据只需简单的调用线程的write()

方法。 线程的cancel()方法时很重要的,以便连接可以在任何时候通过关闭BluetoothSocket来终止。它应该总在处理完

Bluetooth连接后被调用。

使用配置文件

从Android 3.0开始,Bluetooth API就包含了对Bluetooth profiles的支持。 Bluetooth profile是基于蓝牙的设备之间通信的

无线接⼝规范。 例如Hands-Free profile(免提模式)。 如果移动电话要连接⼀个无线耳机,他们都要支持Hands-Free

profile。

你在你的类里可以完成BluetoothProfile接⼝来支持某⼀Bluetooth profiles。Android Bluetooth API完成了下面的Bluetooth

profile:

耳机。 Headset profile提供了移动电话上的Bluetooth耳机支持。Android提供了BluetoothHeadset类,它是⼀个协议,用

来通过IPC(interprocess communication)控制Bluetooth Headset Service。BluetoothHeadset既包含Bluetooth

Headset profile也包含Hands-Free profile,还包括对AT命令的支持。

A2DP. Advanced Audio Distribution Profile (A2DP) profile,高级音频传输模式。Android提供了BluetoothA2dp类,这是

⼀个通过IPC来控制Bluetooth A2DP的协议。

Android4.0(API级别14)推出了支持蓝牙医疗设备模式(HDP),这使您可以创建支持蓝牙的医疗设备,使用蓝牙通信的应

用程序,例如心率监视器,血液,温度计和秤等等。 支持的设备和相应的设备数据专业化代码,请参阅蓝牙分配在

www.bluetooth.org数。请注意,这些值的ISO / IEEE11073-20601引用[7] MDC_DEV_SPEC_PROFILE_* 命名代码附件

的规范。 对于更多的HDP讨论, 查看Health Device Profile.

下面是使用profile的基本步骤:

1.获取默认的Bluetooth适配器。

2.使用getProfileProxy()来建立⼀个与profile相关的profile协议对象的连接。在下面的例⼦中,profile协议对象

是BluetoothHeadset的⼀个实例。

3.设置BluetoothProfile.ServiceListener。该listener通知BluetoothProfile IPC客户端,当客户端连接或断连服务器的时候

4.在[android.bluetooth.BluetoothProfile)

onServiceConnected()](http://docs.eoeandroid.com/reference/android/bluetooth/BluetoothProfile.ServiceListener.html#onServiceConnected(int,)

内,得到⼀个profile协议对象的句柄。

5.⼀旦拥有了profile协议对象,就可以用它来监控连接的状态,完成于该profile相关的其他操作。

例如,下面的代码片段显示如何连接到⼀个BluetoothHeadset协议对象,用来控制Headset profile:


BluetoothHeadset mBluetoothHeadset;

// Get the default adapter

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// Establish connection to the proxy.

mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);

private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {

public void onServiceConnected(int profile, BluetoothProfile proxy) {

if (profile == BluetoothProfile.HEADSET) {

mBluetoothHeadset = (BluetoothHeadset) proxy;

}

}

public void onServiceDisconnected(int profile) {

if (profile == BluetoothProfile.HEADSET) {

mBluetoothHeadset = null;

}

}

};

// ... call functions on mBluetoothHeadset

// Close proxy connection after use.

mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset)

Vendor-specific AT  指令

从Android 3.0开始,应用程序可以注册侦听预定义的Vendor-specific AT命令这样的系统广播(如Plantronics +XEVENT

command)。例如,应用可以接收到⼀个广播,该广播表明连接的设备电量过低,然后通知用户做好其他需要的操作。

创建⼀个带ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent的broadcast receiver来为耳机处理

医疗设备模式

Android4.0(API级别14)推出了支持蓝牙医疗设备模式(HDP),这使您可以创建支持蓝牙的医疗设备,使用蓝牙通信的应

用程序,例如心率监视器,血液,温度计和秤。蓝牙卫生API包括基础

类BluetoothHealth,BluetoothHealthCallback,BluetoothHealthAppConfiguration。 在使用蓝牙卫生API,它有助于理解

这些关键的HDP概念:

创建⼀个 HDP  应用

创建 ⼀个Android HDP应用要下面⼏步:

1.获取⼀个参考的BluetoothHealth代理对象.

类似普通的耳机和A2DP设备,你必须调用BluetoothProfile与getProfileProxy() 。ServiceListener 和医疗配置类型来建

立⼀个配置代理对象的连接。

2.创建⼀个BluetoothHealthCallback和注册的应用程序配置(BluetoothHealthAppConfiguration)作为⼀个医疗sink。

3.建立⼀个连接到医疗设备。⼀些设备将初始化连接。 开展这⼀步对于这些设备,这是不必要的。

4.当连接成功到⼀个医疗设备时,使用文件描述符读/写到医疗设备。

接收到的数据需要使用健康管理,实现了IEEE11073-XXXXX规范进行解释。

5.当完成后,关闭医疗通道和注销申请。通道也有延伸静止时关闭。

为了完善这个例⼦说明这些步骤。查看Bluetooth HDP (Health Device Profile) 。

你可能感兴趣的:(Android 蓝牙开发详解)