主要内容概况
前面我记录了开发蓝牙2.0的过程,今天准备介绍现在的主流蓝牙技术,蓝牙4.0的开发,该蓝牙技术是在Android4.3(API级别18)版本引入的API。
具体的区别主要以下几点:
1.新的蓝牙技术提供了连接服务的方法,以前是没有提供连接蓝牙的方法的。
2.扫描和连接设备,通过回掉的方式来进行通知,不再是以前通过系统广播的方式。
3.低功耗说明使用4.0的蓝牙能降低电量消耗。
4.数据交互的方式也不再是通过stock的方式,而是通过GATT协议。
开始介绍主要的开发内容
开发过程主要还是参考谷歌的官方案例,samples很简单主要就四个类。
下面开始正式上代码:
- 常规的,开启蓝牙,权限添加,设置设备可见等都不介绍了,可以看一下我以前那一篇文章。
我们从设备扫描开始介绍,代码如下:
//获取一下蓝牙适配器
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//开始进行扫描设备了,传递进去一个监听
mBluetoothAdapter.startLeScan(mLeScanCallback);
//再回掉里去接收扫描到的设备
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//这里要注意,会扫描到相同的设备,需要对列表进行去重复
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
//在add的方法中需要进行判断
public void addDevice(BluetoothDevice device) {
if (!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
以上基本上就可以完成扫描设备,并且显示刀列表上了。是不是很简单,连接设备部分会比较麻烦一些。
- 连接设备部分主要可以分为两个部分,一部分先是进行设备匹配;另一部分则是进行连接服务。
官方的demo上绑定一个服务,然后一些连接操作都放到服务中进行,实际我们可以直接调用连接设备的方法。
//连接设置,并传入一个连接的回掉。
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);
//6.0的设备中添加了新的参数,注解意思是首选连接的远程设备的类型,这里我们选择的是低功率蓝牙设备
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback,BluetoothDevice.TRANSPORT_LE);
然后主要的代码逻辑都是在我们的监听事件上,收到连接成功的监听,要去解析服务,这里要做的事情还有要查看链接的设备是否支持有对应的服务在BluetoothGatt里面。
下面这里上具体的代码,还是参照官方例子,稍微改进一下。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
LogUtil.i("链接上");
//连接上设备更新一下UI相关
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
LogUtil.i("断开链接");
//断开设备也更行一下,如果需要重连接,可以在这里进行。
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
LogUtil.i("解析服务");
//获取到支持的服务列表,下面代码块进行解析
displayGattServices(mBluetoothLeService.getSupportedGattServices());
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(characteristic);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
mConnectionState = STATE_CONNECTED;
broadcastUpdate(characteristic);
}
};
- 具体解析服务的地方主要是对设备的一个匹配,中心设备和外围设备有相同的UUID然后就可以识别上了。有一些常见的UUID可以了解一下。
//这里解析服务相关。主要是两个循环,一个是匹配服务的UUID,另外一个则是匹配特征的UUID,如果我们只匹配特定的设备,这里的UUID可以是一个固定的String(UUID)字符串。
for (BluetoothGattService gattService : gattServices) {
HashMap<String, String> currentServiceData = new HashMap<String, String>();
//获取每个服务的uuid
uuid = gattService.getUuid().toString();
//匹配我们的uuid,只要不匹配的姐跳过继续匹配
if (!"某一个uuid".equals(s.getUuid().toString())) {
continue;
}
// 上面服务匹配成功后,在匹配特征,其实和上面一样,拿到特定的uuid,匹配上后就可以发送数据了
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString();
//匹配我们的uuid,只要不匹配的姐跳过继续匹配
if (!"某一个uuid".equals(s.getUuid().toString())) {
continue;
}
//发送特征通知,是否启用还特征设备通知。
setCharacteristicNotification(bc, true);
}
}
这里连接成功后,再把我们的具有我们指定特征的设备注册到远程的设备上去,这里我理解是给远程设备发放了一个通行证,这样你的数据才能发送到我(手机或中心设备)的上面。
/**
* Enables or disables notification on a give characteristic.
*
* @param characteristic Characteristic to act on.
* @param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
//构造出一个gatt的描述符号,注册到
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//Write the value of a given descriptor to the associated remote device.
//这里就是类似的把描述符写到远程的设备上了。
mBluetoothGatt.writeDescriptor(descriptor);
}
}
- 完成上面所有的步骤,基本上中心设备和远程设备已经可以进行数据的传递了。
当发生数据变化的时候回去回掉我们最开始注册的mGattCallback方法中的实现。然后收到通知或者是read的消息的时候,获取到收到的数据。
//mGattCallback中的以下两个方法在发生数据变化的时候会被调用。然后我们在这里就可一拿到发送过来的数据了。
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(characteristic);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
mConnectionState = STATE_CONNECTED;
broadcastUpdate(characteristic);
}
- 调用broadcastUpdate方法统一处理收到的通知或者数据,下面我们其实只关注自己指定的UUID设备就可以了,因为我们自己的应用也只是收集自己的远程设备的消息数据。
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.
//对于其他的远程设备,我们不关心,这里可以不用实现
}
}
//这里我们直接拿到上面heartRate数据就可了。拿到数据后就可以回到我们正常的业务逻辑。
sendBroadcast(intent);
}
完成上述的几个大的步骤基本上你的远程设备和手机已经连接上了,总体来说整个过程还是比较容易。
蓝牙的内容也就到此结束了,如果有问题欢迎反馈给我,其实这个过程就是参考谷歌的官方demo来几率。当我们接触一个新的开发领域的时候,首先想到的应该就是跟着官方文档和demo走,防止自己误入歧途