Android平台包含了对蓝牙网络协议栈的支持,它容许一个蓝牙设备跟其余的蓝牙设备进行无线的数据交换。应用程序经过Android蓝牙API提供访问蓝牙的功能。这些API会把应用程序无线链接到其余的蓝牙设备上,具备点到点和多点无线特征。
使用蓝牙API,Android应用程序可以执行如下功能:
1. 扫描其余蓝牙设备
2. 查询本地已经配对的蓝牙适配器
3. 创建RFCOMM通道
4. 经过服务发现来链接其余设备
5. 在设备间传输数据
6. 管理多个蓝牙链接
基础
本文介绍如何使用Android的蓝牙API来完成使用蓝牙通讯所须要的四项主要任务:设置蓝牙、查找已配对或区域内可用的蓝牙设备、链接设备、设备间传输数据。
全部的可用的API都在android.bluetooth包中。如下概要的介绍建立蓝牙链接所须要的类和接口:
BluetoothAdapter
表明本地蓝牙适配器(蓝牙无线)。BluetoothAdapter是全部蓝牙交互的入口。使用这个类,你可以发现其余的蓝牙设备,查询已配对设备的列表,使用已知的MAC地址来实例化一个BluetoothDevice对象,而且建立一个BluetoothServerSocket对象来监听与其余设备的通讯。
BluetoothDevice
表明一个远程的蓝牙设备。使用这个类经过BluetoothSocket或查询诸如名称、地址、类和配对状态等设备信息来请求跟远程设备的链接。
BluetoothSocket
表明蓝牙socket的接口(相似TCP的Socket)。这是容许一个应用程序跟另外一个蓝牙设备经过输入流和输出流进行数据交换的链接点。
BluetoothServerSocket
表明一个打开的监听传入请求的服务接口(相似于TCP的ServerSocket)。为了链接两个Android设备,一个设备必须用这个类打开一个服务接口。当远程蓝牙设备请求跟本设备创建链接请求时,BluetoothServerSocket会在链接被接收时返回一个被链接的BluetoothSocket对象。
BluetoothClass
描述了蓝牙设备的通常性特征和功能。这个类是一个只读的属性集,这些属性定义了设备的主要和次要设备类和服务。可是,这个类并不保证描述了设备所支持的全部的蓝牙配置和服务,可是这种对设备类型的提示是有益的。
BluetoothProfile
表明一个蓝牙配置的接口。蓝牙配置是基于蓝牙通讯的设备间的无线接口规范。一个例子是免提的配置。更多的配置讨论,请看下文的用配置来工做。
BluetoothHeadset
提供对使用蓝牙耳机的移动电话的支持。它同时包含了Bluetooth Headset和Hands-Free(v1.5)的配置。
BluetoothA2dp
定义如何把高品质的音频经过蓝牙链接从一个设备流向另外一个设备。“A2DP”是Advanced Audio Distribution Profile的缩写。
BluetoothHealth
表明一个健康保健设备配置的控制蓝牙服务的代理。
BluetoothHealthCallback
用于实现BluetoothHealth回调的抽象类。你必须继承这个类,并实现它的回调方法,来接收应用程序的注册状态和蓝牙通道状态变化的更新。
BluetoothHealthAppConfiguration
表明蓝牙相关的第三方健康保健应用程序所注册的与远程蓝牙健康保健设备进行通讯的配置。
BluetoothProfile.ServiceListener
BluetoothProfile IPC客户端链接或断开服务的通知接口(它是运行特俗配置的内部服务)。
Android的联通性---Bluetooth(二)
蓝牙权限
为了在你的应用程序中使用蓝牙功能,至少要声明两个蓝牙权限(BLUETOOTH和BLUETOOTH_ADMIN)中的一个。
为了执行任何蓝牙通讯(如请求链接、接收链接和传输数据),你必须申请BLUETOOTH权限。
为了启动设备发现或维护蓝牙设置,你必须申请BLUETOOTH_ADMIN权限。大多数须要这个权限的应用程序,仅仅是为可以发现本地的蓝牙设备。这个权限所授予的其余能力应该不被使用,除非是电源管理的应用程序,它会在依据用户的请求来修改蓝牙设置。注意:若是你使用了BLUETOOTH_ADMIN权限,那么必需要有BLUETOOTH权限。
在你的应用程序清单文件中声明蓝牙权限,例如:
...
< /manifest>
关于声明应用程序权限的更多信息,请参阅
设置蓝牙
在应用程序可以利用蓝牙通道通讯以前,须要确认设备是否支持蓝牙通讯,若是支持,要确保它是可用的。
若是不支持蓝牙,那么你应该有好的禁用全部蓝牙功能。若是支持蓝牙,可是被禁用的,那么你要在不离开你的应用程序的状况下,请求用户启用蓝牙功能,这种设置要使用BluetoothAdapter对象,在如下两个步骤中完成。
1. 得到BluetoothAdapter对象
BluetoothAdapter对象是全部蓝牙活动都须要的,要得到这个对象,就要调用静态的getDefaultAdapter()方法。这个方法会返回一个表明设备本身的蓝牙适配器的BluetoothAdapter对象。整个系统有一个蓝牙适配器,你的应用程序可以使用这个对象来进行交互。若是getDefaultAdapter()方法返回null,那么该设备不支持蓝牙,你的处理也要在此结束。例如:
BluetoothAdapter mBluetoothAdapter =BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter ==null){
// Device does not support Bluetooth
}
2. 启用蓝牙功能
接下来,你须要确保蓝牙是可用的。调用isEnabled()方法来检查当前蓝牙是否可用。若是这个方法返回false,那么蓝牙是被禁用的。要申请启用蓝牙功能,就要调用带有ACTION_REQUEST_ENABLE操做意图的startActivityForResult()方法。它会给系统设置发一个启用蓝牙功能的请求(不终止你的应用程序)。例如:
if(!mBluetoothAdapter.isEnabled()){
Intent enableBtIntent =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
这时会显示一个请求用户启用蓝牙功能的对话框,如图1所示:
图1.启用蓝牙功能的对话框。
若是用户响应“Yes”,那么系统会开始启用蓝牙功能,完成启动过程(有可能失败),焦点会返回给你的应用程序。
传递给startActivityForResult()方法的REQUEST_ENABLE_BT常量,是一个你的应用程序中定义的整数(它必须大于0),系统会把它做为requestCode参数返回到你的onActivityResult()回调实现中。
若是蓝牙功能启用成功,你的Activity会在onActivityResult()回调中接收到RESULT_OK结果,若是蓝牙没有被启动(或者用户响应了“No”),那么该结果编码是RESULT_CANCELED。
可选地,你的应用程序还能够监听ACTION_STATE_CHANGED广播Intent,不管蓝牙状态什么时候改变,系统都会广播这个Intent。这个广播包含的附加字段EXTRA_STATE和EXTRA_PREVIOUS_STATE中分别指明了新的和旧的蓝牙状态。这些附加字段中可能的值是:STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF和STATE_OFF。监听这个广播对于在应用程序运行时检测蓝牙的状态是有用的。
提示:启用可发现能力会自动启动蓝牙功能。若是你计划在执行蓝牙活动以前,要始终启用设备的可发现机制,就能够跳过上面的第2步,详细请参阅下文“启用蓝牙可发现”。
Android的联通性---Bluetooth(三)
查找设备
使用BluetoothAdapter对象,可以经过设备发现或查询已配对的设备列表来找到远程的蓝牙设备。
设备发现是一个扫描过程,该过程搜索本地区域内可用的蓝牙设备,而后请求一些彼此相关的一些信息(这个过程被叫作“发现”、“查询”或“扫描”)。可是,本地区域内的蓝牙设备只有在它们也启用了可发现功能时,才会响应发现请求。若是一个设备是可发现的,那么它会经过共享某些信息(如设备名称、类别和惟一的MAC地址)来响应发现请求。使用这些信息,执行发现处理的设备可以有选择的初始化跟被发现设备的链接。
一旦跟远程的设备创建的首次链接,配对请求就会自动的被展示给用户。当设备完成配对,相关设备的基本信息(如设备名称、类别和MAC地址)就会被保存,并可以使用蓝牙API来读取。使用已知的远程设备的MAC地址,在任什么时候候都可以初始化一个链接,而不须要执行发现处理(假设设备在可链接的范围内)。
要记住配对和链接之间的差别。配对意味着两个设备对彼此存在性的感知,它们之间有一个共享的用于验证的链接密钥,用这个密钥两个设备之间创建被加密的链接。链接意味着当前设备间共享一个RFCOMM通道,而且可以被用于设备间的数据传输。当前Android蓝牙API在RFCOMM链接被创建以前,要求设备之间配对。(在使用蓝牙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地址。在这个例子中,MAC地址被做为ArrayAdapter的一部分来保存,并显示给用户。随后,该MAC地址可以被提取用于初始化链接。
发现设备
简单的调用startDiscovery()方法就能够开始发现设备。该过程是异步的,而且该方法会当即返回一个布尔值来指明发现处理是否被成功的启动。一般发现过程会查询扫描大约12秒,接下来获取扫描发现的每一个设备的蓝牙名称。
为了接收每一个被发现设备的的信息,你的应用程序必须注册一个ACTION_FOUND类型的广播接收器。对应每一个蓝牙设备,系统都会广播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
警告:执行设备发现,对于蓝牙适配器来讲是一个沉重的过程,它会消耗大量的资源。一旦发现要链接设备,在尝试链接以前必定要确认用cancelDiscovery()方法来终止发现操做。另外,若是已经有一个跟设备的链接,那么执行发现会明显的减小链接的可用带宽,所以在有链接的时候不该该执行发现处理。
Android的联通性---Bluetooth(四)
启用设备的可发现性
若是要让本地设备能够被其余设备发现,那么就要调用ACTION_REQUEST_DISCOVERABLE操做意图的startActivityForResult(Intent, int)方法。这个方法会向系统设置发出一个启用可发现模式的请求(不终止应用程序)。默认状况下,设备的可发现模式会持续120秒。经过给Intent对象添加EXTRA_DISCOVERABLE_DURATION附加字段,能够定义不一样持续时间。应用程序可以设置的最大持续时间是3600秒,0意味着设备始终是可发现的。任何小于0或大于3600秒的值都会自动的被设为120秒。例如,如下代码把持续时间设置为300秒:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
如图2所示,申请用户启用设备的可发现模式时,会显示这样一个对话框。若是响应“Yes”,那么设备的可发现模式会持续指定的时间,并且你的Activity会接收带有结果代码等于可发现设备持续时间的onActivityResult()回调方法的调用。若是用户响应“No”或有错误发生,则结果代码等于RESULT_CANCELED.
图2.启用可发现性对话框。
注意:若是设备没有开启蓝牙功能,那么开启设备的可发现模式会自动开启蓝牙。
在可发现模式下,设备会静静的把这种模式保持到指定的时长。若是你想要在可发现模式被改变时得到通知,那么你能够注册一个ACTION_SCAN_MODE_CHANGED类型的Intent广播。这个Intent对象中包含了EXTRA_SCAN_MODE和EXTRA_PREVIOUS_SCAN_MODE附加字段,它们会分别告诉你新旧扫描模式。它们每一个可能的值是:SCAN_MODE_CONNECTABLE_DISCOVERABLE,SCAN_MODE_CONNECTABLE或SCAN_MODE_NONE,它们分别指明设备是在可发现模式下,仍是在可发现模式下但依然可接收链接,或者是在可发现模式下并不能接收链接。
若是你要初始化跟远程设备的链接,你不须要启用设备的可现性。只有在你想要把你的应用程序做为服务端来接收输入链接时,才须要启用可发现性,由于远程设备在跟你的设备链接以前必须可以发现它。
链接设备
为了让两个设备上的两个应用程序之间创建链接,你必须同时实现服务端和客户端机制,由于一个设备必须打开服务端口,同时另外一个设备必须初始化跟服务端设备的链接(使用服务端的MAC地址来初始化一个链接)。当服务端和客户端在相同的RFCOMM通道上有一个BluetoothSocket链接时,才可以被认为是服务端和客户端之间创建了链接。这时,每一个设备可以得到输入和输出流,而且可以彼此开始传输数据。
服务端设备和客户端设备彼此获取所需的BluetoothSocket的方法是不一样的。服务端会在接收输入链接的时候接收到一个BluetoothSocket对象。客户端会在打开跟服务端的RFCOMM通道时接收到一个BluetoothSocket对象。
一种实现技术是自动的准备一个设备做为服务端,以便在每一个设备都会有一个服务套接字被打开,并监听链接请求。当另外一个设备初始化一个跟服务端套接字的链接时,它就会变成一个客户端。另外一种方法,一个设备是明确的”host”链接,而且根据要求打开一个服务套接字,而其余的设备只是简单的初始化链接。
注意:若是两个设备以前没有配对,那么Android框架会在链接过程期间,自动的显示一个配对请求通知或对话框给用户,如图3所示。所以在试图链接设备时,你的应用程序不须要关心设备是否被配对。FRCOMM的尝试性链接会一直阻塞,一直到用户成功的配对,或者是因用户拒绝配对或配对超时而失败。
图3.蓝牙配对对话框
Android的联通性---Bluetooth(五)
做为链接的服务端
当你想要链接两个设备时,一个必须经过持有一个打开的BluetoothServerSocket对象来做为服务端。服务套接字的用途是监听输入的链接请求,而且在一个链接请求被接收时,提供一个BluetoothSocket链接对象。在从BluetoothServerSocket对象中获取BluetoothSocket时,BluetoothServerSocket可以(而且也应该)被废弃,除非你想要接收更多的链接。
如下是创建服务套接字和接收一个链接的基本过程。
1. 调用listenUsingRfcommWithServiceRecord(String, UUID)方法来得到一个BluetoothServerSocket对象。该方法中的String参数是一个可识别的你的服务端的名称,系统会自动的把它写入设备上的Service Discovery Protocol(SDP)数据库实体(该名称是任意的,而且能够简单的使用你的应用程序的名称)。UUID参数也会被包含在SDP实体中,而且是跟客户端设备链接的基本协议。也就是说,当客户端尝试跟服务端链接时,它会携带一个它想要链接的服务端可以惟一识别的UUID。只有在这些UUID彻底匹配的状况下,链接才可能被接收。
2. 经过调用accept()方法,启动链接请求。这是一个阻塞调用。只有在链接被接收或发生异常的状况下,该方法才返回。只有在发送链接请求的远程设备所携带的UUID跟监听服务套接字所注册的一个UUID匹配的时候,该链接才被接收。链接成功,accept()方法会返回一个被链接的BluetoothSocket对象。
3. 除非你想要接收其余链接,不然要调用close()方法。该方法会释放服务套接字以及它所占用的全部资源,但不会关闭被链接的已经有accept()方法所返回的BluetoothSocket对象。跟TCP/IP不同,每一个RFCOMM通道一次只容许链接一个客户端,所以在大多数状况下,在接收到一个链接套接字以后,当即调用BluetoothServerSocket对象的close()方法是有道理的。
accept()方法的调用不该该在主Activity的UI线程中被执行,由于该调用是阻塞的,这会阻止应用程序的其余交互。一般在由应用程序所管理的一个新的线程中来使用BluetoothServerSocket对象或BluetoothSocket对象来工做。要终止诸如accept()这样的阻塞调用方法,就要从另外一个线程中调用BluetoothServerSocket对象(或BluetoothSocket对象)的close()方法,这时阻塞会当即返回。注意在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对象时,套接字已是被链接的,所以你不该该再调用像客户端那样调用connect()方法了。
应用程序中的manageConnectedSocket()方法是一个自定义方法,它会初始化用于传输数据的线程。
一般,一旦你监听完成呼入链接,就应该关闭BluetoothServerSocket对象。在这个示例中,close()方法是在得到BluetoothSocket对象以后被调用的。你可能还想要提供一个公共方法,以便在你的线程中可以关闭你想要终止监听的服务套接字事件中的私有BluetoothSocket对象。
做为链接的客户端
为了初始化一个与远程设备(持有打开的服务套接字的设备)的链接,首先必须获取个表明远程设备的BluetoothDevice对象。而后使用BluetoothDevice对象来获取一个BluetoothSocket对象,并初始化该链接。
如下是一个基本的链接过程:
1. 经过调用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)方法,得到一个BluetoothSocket对象。这个方法会初始化一个链接到BluetoothDevice对象的BluetoothSocket对象。传递给这个方法的UUID参数必须与服务端设备打开BluetoothServerSocket对象时所使用的UUID相匹配。在你的应用程序中简单的使用硬编码进行比对,若是匹配,服务端和客户端代码就能够应用这个BluetoothSocket对象了。
2. 经过调用connect()方法来初始化链接。在这个调用中,为了找到匹配的UUID,系统会在远程的设备上执行一个SDP查询。若是查询成功,而且远程设备接收了该链接请求,那么它会在链接期间共享使用RFCOMM通道,而且connect()方法会返回。这个方法是一个阻塞调用。若是由于某些缘由,链接失败或链接超时(大约在12秒以后),就会抛出一个异常。
由于connect()方法是阻塞调用,这个链接过程始终应该在独立与主Activity线程以外的线程中被执行。
注意:在调用connect()方法时,应该始终确保设备没有正在执行设备发现操做。若是是在发现操做的过程当中,那么链接尝试会明显的变慢,而且更像是要失败的样子。
示例
如下是初始化蓝牙链接线程的一个基本的例子:
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()方法来进行清理工做。这样作会当即关闭被链接的套接字,并清理全部的内部资源。
Android的联通性---Bluetooth(六)
管理链接
当你成功的链接了两个(或更多)设备时,每个设备都有一个被链接的BluetoothSocket对象。这是良好的开始,由于你可以在设备之间共享数据。使用BluetoothSocket对象来传输任意数据的过程是简单的:
1. 分别经过getInputStream()和getOutputStream()方法来得到经过套接字来处理传输任务的InputStream和OutputStream对象;
2. 用read(byte[])和write(byte[])方法来读写流中的数据。
固然,有更多实现细节要考虑。首先和前提,对于全部数据流的读写应该使用专用的线程。这是重要的,由于read(byte[])和write(byte[])方法是阻塞式调用。Read(byte[])方法在从数据流中读取某些数据以前一直是阻塞的。write(byte[])方法一般是不阻塞的,可是对于流的控制,若是远程设备不是足够快的调用read(byte[])方法,而且中间缓存被填满,那么write(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,而后返回继续等待流中的更多的字节。
发送输出数据就像在主Activity中调用线程的write()方法同样简单,而且给该方法传入要发送的字节。而后这个方法简单的调用write(byte[])方法把数据发送给远程设备。
线程的cancel()方法是相当重要的,以便该链接在任什么时候候可以经过关闭BluetoothSocket对象来终止。这个方法在使用完蓝牙链接时应该始终被调用。
使用配置来工做
从Android3.0开始,蓝牙API就包含了对用蓝牙配置来进行工做的支持。Bluetooth Profile是设备之间基于蓝牙进行通讯的无线接口规范。其中一个示例是Hands-Free配置。对于一个链接到无线耳机的移动电话,移动电话和蓝牙耳机都必须支持Hands-Free配置。
你可以实现BluetoothProfile接口,来编写你本身的支持特殊蓝牙配置的类。Android蓝牙API提供了对如下蓝牙配置的实现:
· Headset(耳机):该配置提供了对用于移动电话的蓝牙耳机的支持。Android提供了BluetoothHeadset类,这个类是一个代理,它经过进程间通讯(IPC)来控制蓝牙耳机服务。它包含了Bluetoot Headset和Hands-Free(v1.5)配置。BluetoothHeadset类包含了对AT命令的支持。关于AT命令的更多讨论,请看“Vendor-specific AT commands”。
· A2DP:Advanced Audio Distribution Profile的缩写。它定义如何流化高品质的音频,并让音频流在经过蓝牙链接在设备之间传输。Android提供了BluetoothA2dp类,它一个经过IPC来控制的蓝牙A2DP服务的代理。
· Health Device:Android4.0(API Level 14)中引入了对Bluetooth Health Device Profile(HDP)的支持。它可以让你建立一个跟支持蓝牙的健康保健设备进行蓝牙通讯的应用程序,这些健康保健包括:心率监护仪、血压测量仪、体温计、体重秤等。对应它支持的设备的列表和它们相应的设备数据规范,蓝牙联盟的网站:www.bluetooth.org。注意,这些值也是参考了ISO/IEEE11073-20601[7]规范中命名编码规范附件中的名为MDC_DEV_SPEC_PROFILE_*规范。
如下是使用配置进行工做的基本步骤
1. 获取默认的适配器;
2.使用getProfileProxy()方法来创建一个与配置相匹配的配置代理对象的链接。在下面的示例中,配置代理对象是一个BluetoothHeadset对象实例;
3.建立一个BluetoothProfile.ServiceListener监听器,该监听器会在它们链接到服务器或中断与服务器的链接时,通知BluetoothProfile的IPC客户端。
4.在onServiceConnected()事件中,得到对配置代理对象的处理权;
5.一旦得到配置代理对象,就能够用它来监视链接的状态,并执行与配置有关的其余操做。
例如,如下代码片断显示如何链接一个BluetoothHeadset代理对象,以便控制Headset配置:
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);
Android的联通性---Bluetooth(七)
健康设备配置
Android4.0(APILevel 14)中引入了对Bluetooth Health Device Profile(HDP)支持,这回让你建立跟支持蓝牙的健康设备进行蓝牙通讯的应用程序,如心率监护仪、血压测量仪、体温计、体重秤等。Bluetooth Health API包含了BluetoothHealth、BluetoothHealthCallbackhe和BluetoothHealthAppConfiguration等类。
在使用的Bluetooth Health API中,有助于理解如下关键的HDP概念:
概念
介绍
Source
HDP中定义的一个角色,一个Source是一个把医疗数据(如体重、血弹、体温等)传输给诸如Android手机或平板电脑等的设备,
Sink
HDP中定义的一个角色,在HDP中,一个Sink是一个接收医疗数据的小设备。在一个Android HDP应用程序中,Sink用BluetoothHealthAppConfiguration对象来表明。
Registration
指的是给特定的健康设备注册一个Sink。
Connection
指的是健康设备和Android手机或平板电脑之间打开的通讯通道。
建立HDP应用程序
如下是建立Android HDP应用中所涉及到的基本步骤:
1. 得到BluetoothHealth代理对象的引用。
相似于常规的耳机和A2DP配置设备,必须调用带有BluetoothProfile.ServiceListener和HEALTH配置类型参数的getProfileProxy()方法来创建与配置代理对象的链接,
2. 建立BluetoothHealthCallback对象,并注册一个扮演Health Sink角色的应用程序配(BluetoothHealthAppConfiguration)。
3. 创建跟健康设备的链接。某些设备会初始化链接,在这样的设备中进行这一个步是没有必要的。
4. 当成功的链接到健康设备时,就可使用文件描述来读写健康设备。所接收到的数据须要使用健康管理器来解释,这个管理器实现了IEEE 11073-xxxxx规范。
5. 完成以上步骤后,关闭健康通道,并注销应用程序。该通道在长期被闲置时,也会被关闭。android