前言
舍友是做小鸣单车app的,周末有空,找他了解一下蓝牙开锁的实现过程,整理成了一个简单的demo,方便以后做蓝牙硬件方面的项目。
蓝牙api简介:
BluetoothAdapter
BluetoothAdapter 拥有基本的蓝牙操作,例如开启蓝牙扫描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)实例化一个 BluetoothDevice 用于连接蓝牙设备的操作等等。
BluetoothDevice
代表一个远程蓝牙设备。这个类可以让你连接所代表的蓝牙设备或者获取一些有关它的信息,例如它的名字,地址和绑定状态等等。
BluetoothGatt
这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。
BluetoothGattService
这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。
BluetoothGattCharacteristic
这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。
开发步骤:
一、声明权限
<!--使用蓝牙所需要的权限-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!--使用扫描和设置蓝牙的权限(申明这一个权限必须申明上面一个权限)-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--在Android5.0之前,是默认申请GPS硬件功能的。而在Android 5.0 之后,需要在manifest 中申明GPS硬件模块功能的使用。-->
<uses-feature android:name="android.hardware.location.gps" />
<!--在 Android 6.0 及以上,还需要打开位置权限。如果应用没有位置权限,蓝牙扫描功能不能使用(其它蓝牙操作例如连接蓝牙设备和写入数据不受影响)。-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
二、蓝牙初始化
// 判断是否支持蓝牙
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "您的设备不支持蓝牙4.0", Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// 判断是否开启蓝牙,没有就开启
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
}
三、扫描和连接蓝牙
// 开始扫描蓝牙,获取蓝牙对象
mBluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.e("=====", "扫秒到的设备------" + device.getAddress());
//下面这个mac地址是固定硬件的,做项目的时候需要按需修改
if (device.getAddress().equals("50:8C:B1:5A:42:F7")) {
// 停止蓝牙扫描,扫描耗资源,不能一直扫
mBluetoothAdapter.stopLeScan(this);
// 开始连接蓝牙设备
mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback);
Log.e("===========", "开始连接.....");
}
}
});
回调方法中,第一个参数是代表蓝牙设备的类,可以通过这个类建立蓝牙连接获取关于这一个设备的一系列详细的参数,例如名字,MAC 地址等等;第二个参数是蓝牙的信号强弱指标,通过蓝牙的信号指标,我们可以大概计算出蓝牙设备离手机的距离。计算公式为:d = 10^((abs(RSSI) - A) / (10 * n));第三个参数是蓝牙广播出来的广告数据。
四、监听蓝牙回调,操作读写数据
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
// 蓝牙连接状态回调
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
// 连接成功,调用discoverServices()发现服务
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.w("TAG", "connected");
gatt.discoverServices();
}
// 连接断开,调用close()释放资源
else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.w("TAG", "disconnetce");
mBluetoothGatt.close();
mBluetoothGatt = null;
}
}
// 发现服务回调
// 到这一步,我们已经成功和蓝牙设备建立了可通信的连接,接下来就可以执行相应的蓝牙通信操作了,
// 例如写入数据,读取蓝牙设备的数据等等
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.w("TAG", "onServicesDiscovered");
if (status == BluetoothGatt.GATT_SUCCESS) {
readBatrery(); //读取电量操作
sendSetting(); //下发配置值
// BLE app通常需要获取设备中characteristic 变化的通知
// 其实就是一般我们都是通过手机向设备读写数据,但是如果想让设备数据改变时,主动往手机发送数据,就需要
// 1.开启 Android 端接收通知的开关
gatt.setCharacteristicNotification(readCharacteristic, true);
// 2.往 Characteristic 的 Descriptor 属性写入开启通知的数据开关
BluetoothGattDescriptor descriptor = readCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
// 小鸣蓝牙锁就是这种情况,往设备写入数据之后,设备不会立马返回结果,
// 在执行完操作之后,通过onCharacteristicChanged主动上报的方式通知app
// 注意:上面这部不是必须的,视硬件而言
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//读取到值,根据UUID来判断读到的是什么值
if (characteristic.getUuid().toString().equals("00002a19-0000-1000-8000-00805f9b34fb")) {
// 获取到电量
int battery = characteristic.getValue()[0];
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(发送值成功),可以根据 characteristic.getValue()来判断是哪个值发送成功了
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// 收到设备notify值 (设备上报值),进行自己的操作
byte[] values = characteristic.getValue();
}
};
void readBatrery() {
//此处的0000180f...是举例,实际开发需要询问硬件那边
BluetoothGattService batteryService = mBluetoothGatt.getService(UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb"));
if (batteryService != null) {
//此处的00002a19...是举例,实际开发需要询问硬件那边
BluetoothGattCharacteristic batteryCharacteristic = batteryService.getCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb"));
if (batteryCharacteristic != null) {
//读取电量, 这是读取batteryCharacteristic值的方法,读取其他的值也是如此,只是它们的ServiceUUID 和CharacUUID不一样
mBluetoothGatt.readCharacteristic(batteryCharacteristic);
}
}
}
void sendSetting() {
//此处的00001805...是举例,实际开发需要询问硬件那边
BluetoothGattService sendService = mBluetoothGatt.getService(UUID.fromString("00001805-0000-1000-8000-00805f9b34fb"));
if (sendService != null) {
//此处的00002a08...是举例,实际开发需要询问硬件那边
BluetoothGattCharacteristic sendCharacteristic = sendService.getCharacteristic(UUID.fromString("00002a08-0000-1000-8000-00805f9b34fb"));
if (sendCharacteristic != null) {
//随便举个数据
sendCharacteristic.setValue(new byte[]{0x01, 0x20, 0x03});
//写命令到设备,
mBluetoothGatt.writeCharacteristic(sendCharacteristic);
}
}
}
这样就大概能完成手机对蓝牙硬件的通信,可以淘宝买一些蓝牙硬件,然后来调试看看。
部分资料参考:Android BLE 蓝牙开发入门