最近可穿戴设备发展的很火,而且蓝牙4.0 以上支持低功耗模式,因此,android4.3(API18)以上支持蓝牙BLE编程。BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。下面介绍android 的BLE开发。
1. 基本概念介绍
- BluetoothManager :管理蓝牙服务的一个类,主要用于得到一个蓝牙适配器BluetoothAdapter。
- BluetoothAdapter类:代表了一个本地的蓝牙适配器。它是所有蓝牙交互的的入口点。利用它你可以发现其他蓝牙设备,查询绑定了的设备,使用已知的MAC地址实例化一个蓝牙设备和建立一个BluetoothServerSocket(作为服务器端)来监听来自其他设备的连接。
- BluetoothDevice类:代表了一个远端的蓝牙设备,使用它请求远端蓝牙设备连接或者获取远端蓝牙设备的名称、地址、种类和绑定状态。
- BluetoothGatt类:符合BLE GATT协议,封装了与其他蓝牙设备通信功能的一个类。可以通过BluetoothDevice的connectGatt(Context, boolean, BluetoothGattCallback)得到其实例。
- GATT(Generic Attribute Profile) 通用属性参数文件,通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。蓝牙 4.0特有。可以理解为一个规范文件。
- ATT(Attribute Protocol) 属性协议。GATT是基于ATTProtocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics 和services的形式传输。
- Service 服务。Characteristic的集合。例如一个service叫做“Heart RateMonitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic
- Characteristic 特性。Characteristic可以理解为一个数据类型,它包括1个value和0至多个Descriptor
- Descriptor 对Characteristic的描述,例如范围、计量单位等,一个Descriptor包含一个Value
其中Service、Characteristic、Descriptor,这三部分是BLE的核心,都由UUID作为唯一标示符。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY。
2. BLE编程
1. 权限配置
由于android4.3及以上才支持BLE,因此需指定编译版本为targetSdkVersion大于等于18。并且需要在AndroidManifest.xml文件中配置如下所示的权限
<uses-permissionandroid:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
2. 编程步骤
1.获取BluetoothManager服务及BluetoothAdapter实例
BluetoothManager是系统管理蓝牙的一种服务,获取了BluetoothManager我们就可以通过其getAdapter()获取蓝牙适配器了。
//1.获取蓝牙管理服务及BluetoothAdapter实例
mBluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null){
return;
}
2.打开蓝牙
接下来调用BluetoothAdapter的isEnabled()判断蓝牙是否打开,未打开我们可以发送一个action为BluetoothAdapter.ACTION_REQUEST_ENABLE的intent来打开蓝牙。
//2. 判断蓝牙是否打开,没有打开就打开
if (!mBluetoothAdapter.isEnabled()){
Intent intent = newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,REQUEST_ENABLE_BT);
}
这时界面上回弹出一个对话框提示我们打开蓝牙,打开成功后会回调Activity的onActivityResult(int requestCode, int resultCode, Intent data)方法,我们可以在里面提示一些信息。比如提示蓝牙已经打开等。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intentdata) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK){
return;
}
if (requestCode == REQUEST_ENABLE_BT){
Toast.makeText(this,"蓝牙已开启",Toast.LENGTH_LONG).show();
}
}
3.扫描周围的BLE蓝牙设备
startLeScan(BluetoothAdapter.LeScanCallbackcallback)即可扫描出BLE设备,在callback中会回调。但是对于5.0以上的系统,android添加了新的API,原有的startLeScan(BluetoothAdapter.LeScanCallback callback)已经被废弃,在5.0以上的系统中是使用BluetoothLeScanner的startScan(ScanCallbackcallback),回调也是ScanCallback了。
/**
* 扫描Bluetooth LE
* @param enable
*/
private void scanBleDevice(boolean enable){
//android 5.0 以前
if (Build.VERSION.SDK_INT < 21){
if (enable){
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScaning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
},SCAN_SECOND);
mScaning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScaning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
} else {
scanner = mBluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(mScanCallback);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
scanner.stopScan(mScanCallback);
}
},SCAN_SECOND);
}
}
扫描的回调如下:
//sacn扫描回调 5.0以上用
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device =result.getDevice();
if (device != null){
//过滤掉其他设备
if (device.getName() != null&& device.getName().startsWith("WINPOS")){
BLEDevice bleDevice = newBLEDevice(device.getName(),device.getAddress());
if(!mBLEDeviceList.contains(bleDevice)){
mBLEDeviceList.add(bleDevice);
showBluetoothLeDevice(bleDevice);
}
}
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
Log.d(TAG,"onBatchScanResults");
}
@Override
public void onScanFailed(int errorCode) {
Log.d(TAG,"onScanFailed");
}
};
//4.3以上
private BluetoothAdapter.LeScanCallback mLeScanCallback = newBluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice bluetoothDevice, int i,byte[] bytes) {
if (bluetoothDevice != null){
//过滤掉其他设备
if (bluetoothDevice.getName()!= null && bluetoothDevice.getName().startsWith("WINPOS")){
BLEDevice bleDevice = newBLEDevice(bluetoothDevice.getName(),bluetoothDevice.getAddress());
if(!mBLEDeviceList.contains(bleDevice)){
mBLEDeviceList.add(bleDevice);
showBluetoothLeDevice(bleDevice);
}
}
}
}
};
这里我预先定义了BLEDevice类,用来表示BLE设备。定义如下:
public class BLEDevice {
private String name;
private String mac;
public BLEDevice(String name, String mac) {
this.name = name;
this.mac = mac;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
@Override
public boolean equals(Object o) {
return !TextUtils.isEmpty(this.mac) &&!TextUtils.isEmpty(((BLEDevice)o).mac)
&&this.mac.equals(((BLEDevice) o).getMac());
}
}
扫描时我将扫描到的设备添加到List<BLEDevice> mBLEDeviceList中并显示出来。
/**
* 显示BLE设备
* @param device
*/
private void showBluetoothLeDevice(BLEDevice device){
if (device == null){
showToast("没有发现BLE设备");
return;
}
mTextView.append(device.getName() + " " + device.getMac() + "\n");
}
4.获取远程设备,连接GATT服务