一.蓝牙硬件操作
Android操作蓝牙需要申请蓝牙权限、定位权限,部分手机还必须要打开GPS才能使用。
蓝牙的打开、关闭、搜索,这部分内容只是简单的调用API就能实现,这里不做说明。
但是从连接开始就要了解一些低功耗蓝牙的知识,这些是在Android以外的知识,现在介绍最基础的使用。
1.连接
public void connect(Context context, BluetoothDevice device)
mBluetoothGatt = device.connectGatt(context, false, mBluetoothGattCallback);
}
这里着重说明BluetoothGatt和BluetoothGattCallback这两个东西,Gatt是一种协议一种规范,低功耗蓝牙就是建立在这个协议之上的通信方式。
二.在连接之后,要把蓝牙看作一个什么样的对象![把蓝牙看作一个树形结构](https://i-blog.csdnimg.cn/blog_migrate/f4e800ac14218f4fdf8fb77c80586df0.png)
调用完连接方法之后,就把蓝牙看作上面这棵树,最重要的就是characteristic节点,characteristic(特征值)是手机与蓝牙设备交互信息的关键。
现在要通过BluetoothGatt这个对象来操纵蓝牙,而通过BluetoothGattCallback这个回调来接收蓝牙的消息和状态变化。下面按照顺序说明,连接后应该做的操作
mBluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status,
final int newState) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
gatt.discoverServices();
}else{...}
}
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS){
//todo 可以去找特征值了
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic, final int status) {
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic, final int status) {
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic) {
//todo 按照协议解析数据,characteristic.getValue()
}
};
1.设备连接状态的变化
在onConnectionStateChange中等待接收连接状态的改变,这里能够接收,连接成功、连接失败、断开等状态。
2.在判断到连接成功之后,发现服务
gatt.discoverServices();
在调用发现服务之后会,这个回调会得到消息onServicesDiscovered,当服务发现之后,就代表我们可以去操作我们需要的service和characteristic了。
3.如何找到要用的characteristic
UUID(通用唯一识别码),蓝牙中用到了这个概念,每一个service,characteristic都有唯一的UUID。我们需要的UUID应该由蓝牙硬件一方提供说明文档,告知做app的开发人员,不同的characteristic可能对应不同的功能。
这是两个很常用的UUID
String UUID_service ="0000FFE0-0000-1000-8000-00805F9B34FB";
String UUID_characteristic ="0000FFE1-0000-1000-8000-00805F9B34FB";
UUID serviceUUID = UUID.fromString(UUID_service);
UUID characteristicUUID = UUID.fromString(UUID_characteristic);
service =bluetoothGatt.getService(serviceUUID);
characteristic =service.getCharacteristic(characteristicUUID);
至此,我们终于拿到了最关键的这个对象,我们可以主动去读一下这个特征值瞧瞧
bluetoothGatt.readCharacteristic(characteristic)
结果会在onCharacteristicRead中得到
三.如何使用特征值与蓝牙设备互发消息
1.向蓝牙设备发送消息
蓝牙与手机的交流都是通过字节数组,当我们修改蓝牙设备特征值,蓝牙就收到了我们发送的这串数据,
characteristic.setValue(byte[] data);
bluetoothGatt.writeCharacteristic(characteristic);
然后就会在回调onCharacteristicWrite中得到我们刚发送的消息的结果,
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic, final int status) {
if (status == BluetoothGatt.GATT_SUCCESS){
}
}
这就证明发送成功了。
2.接收蓝牙设备发送的消息
手机端通过监听特征值的变化,来接收消息,如果蓝牙设备改变了一个特征值的值,被我们监听到了,这就相当于,蓝牙设备向app发送了字节数组。
bluetoothGatt.setCharacteristicNotification(characteristic, true);
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic) {
//todo 按照协议解析数据,characteristic.getValue(),注意:每次传输大小有限制,如果过长,这个回调就会执行多次,要自己把每次的到的数据拼接成一个完整的数据帧
}
每当特征值被蓝牙修改,我们就会在onCharacteristicChanged接收到修改的信息,这就是蓝牙发送消息给app。
3.解析数据
数据的解析,要根据协议来执行,这个协议一般由app的开发和蓝牙硬件的开发人员一起制定。
举例说明:
假设这样制定一个协议,将接收到的字节数组转成十六进制,每个字节的范围就是00-FF
规定每一个完整的数据帧是以BB开头,以7E结尾,数组中第二个位置的数字代表有用数据体的长度。app可以根据这个规定拼接完整数据帧,以及排除一些多余的或者错误的字节
BB 02 A7 64 7E
这个数据帧中代表特别含义的数据体就是 A7 64
其中第一位代表功能标识,其中A7可能就代表这个消息是电池的电量,64表示剩余电量是100%
四.DEMO
private final String TAG = "BLE_TEST";
private String UUID_service = "0000FFE0-0000-1000-8000-00805F9B34FB";
private String UUID_characteristic ="0000FFE1-0000-1000-8000-00805F9B34FB";
private BluetoothGatt mBluetoothGatt;
private BluetoothGattCharacteristic mCharacteristic;
private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
gatt.discoverServices();//四.连接蓝牙成功之后,发现服务
}
super.onConnectionStateChange(gatt, status, newState);
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS){ //五.发现服务成功之后,去找需要的特征值
UUID serviceUUID = UUID.fromString(UUID_service);
UUID characteristicUUID = UUID.fromString(UUID_characteristic);
BluetoothGattService service = mBluetoothGatt.getService(serviceUUID);
mCharacteristic = service.getCharacteristic(characteristicUUID); //找到特征值之后进行收发操作,设置接收特征值通知
mBluetoothGatt.setCharacteristicNotification(mCharacteristic, true);
send(); //发个消息给蓝牙
}
super.onServicesDiscovered(gatt, status);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i(TAG,"onCharacteristicRead" + ByteHelper.BytesToHexString(characteristic.getValue()));
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status); Log.i(TAG,"onCharacteristicWrite:"+ByteHelper.BytesToHexString(characteristic.getValue()));
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.i(TAG,"onCharacteristicChanged"+ByteHelper.BytesToHexString(characteristic.getValue()));
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (VERSION.SDK_INT >= VERSION_CODES.M) {
int checkResult = ContextCompat.checkSelfPermission(this, permission.ACCESS_FINE_LOCATION);
if (checkResult== PackageManager.PERMISSION_DENIED){
ActivityCompat.requestPermissions(this, new String[]{permission.ACCESS_FINE_LOCATION}, 1);
}
}
initBluetooth();
}
private void initBluetooth() {
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothAdapter.enable();//一.打开蓝牙
final BluetoothLeScanner bluetoothLeScanner =bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(new ScanCallback() {//二.搜索蓝牙
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if ("我的蓝牙".equals(result.getDevice().getName())){//三.连接蓝牙
mBluetoothGatt = result.getDevice().connectGatt(getApplicationContext(), false, mBluetoothGattCallback);
bluetoothLeScanner.stopScan(this);
}
}
});
}
private void send() {
byte[] mes = new byte[4];
mes[0] = (byte) 0xAB;
mes[1] = (byte) 0xA8;
mes[2] = (byte) 0x58;
mes[3] = (byte) 0xFE;
mCharacteristic.setValue(mes);
mBluetoothGatt.writeCharacteristic(mCharacteristic);
}
五.总结
这些只是低功耗蓝牙操作的最基本使用,而且只是站在app开发者的角度进行描述。
蓝牙开发还有其他很多可扩展的细节,如:
解析广播包
将手机作为外围设备
单次传输突破20字节限制
通知与指示器的区别
等等等等。。。