每个人对于蓝牙都不陌生,近距离数据传输,方便;可是当你的业务需求需要你第一次接触蓝牙开发的时候,却会发现你对它并不了解;首先,蓝牙发展至今经历了8个版本的更新。1.1、1.2、2.0、2.1、3.0、4.0、4.1、4.2。那么在1.x~3.0之间的我们称之为传统蓝牙,4.x开始的蓝牙我们称之为低功耗蓝牙也就是蓝牙ble,当然4.x版本的蓝牙也是向下兼容的。android手机必须系统版本4.3及以上才支持BLE API。低功耗蓝牙较传统蓝牙,传输速度更快,覆盖范围更广,安全性更高,延迟更短,耗电极低等等优点。(现在的穿戴设备都是使用BLE蓝牙技术的)
传统蓝牙与低功耗蓝牙通信方式也有所不同,传统的一般通过socket方式,而低功耗蓝牙是通过Gatt协议来实现。所以我直接上手了BLE蓝牙。
这三部分都用UUID作为唯一标识符。UUID为这种格式:0000ffe1-0000-1000-8000-00805f9b34fb。比如有3个Service,那么就有三个不同的UUID与Service对应。这些UUID都写在硬件里,我们通过BLE提供的API可以读取到。
一个BLE终端可以包含多个Service, 一个Service可以包含多个Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个Value。Characteristic是比较重要的,是手机与BLE终端交换数据的关键,读取设置数据等操作都是操作Characteristic的相关属性。
如下图我有个BLE的硬件,用android 版本的 BLE Tool(点击下载蓝牙调试助手) 连接上,然后我们可以看到UUID列表,这里每一行的UUID都代表一个Service,再点击任意一行进去,又可以看到一个UUID列表,这里每一行的UUID都代表一个Characteristic,再点击任意一行进去,即可以操作这个Characteristic,比如写入数据或者读出数据等。
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" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
if (null == adapter) {
//蓝牙不支持
}
//还有一种获取方法
//BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter.isEnabled()) {//判断是否打开蓝牙
//mBluetoothAdapter.enable();//无交互打开蓝牙
//系统提示用户选择是否打开蓝牙
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), REQUEST_ENABLE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//代开蓝牙回调
if (requestCode == REQUEST_ENABLE) {
if (resultCode == RESULT_OK) {
//打开成功
} else {
mlog.e("打开蓝牙失败");
}
}
}
现在基本使用第二种方法
// mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索
// mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
//扫描结果回调 不要在这里处理耗时动作
mlog.e(">>>>>>>发现设备 【" + device.getName() + "】 : " + device.getAddress());
}
};
//正确的扫描操作
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
scanHandler.postDelayed(new Runnable() {//10秒后停止扫描 不能长时间不间断的扫描 很好资源的
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback); //停止搜索
}
}, SCAN_PERIOD);
mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索
} else {
mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索
}
}
//连接
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
mlog.e("BluetoothAdapter not initialized or unspecified address.");
return false;
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
mlog.e("??????Device not found. Unable to connect.");
return false;
}
if (mBluetoothGatt != null) {
mlog.e("???????");
mBluetoothGatt.close();
mBluetoothGatt = null;
}
mBluetoothGatt = device.connectGatt(getApplication(), false, mGattCallback); //该函数才是真正的去进行连接
// mBluetoothGatt.connect();
mlog.e("Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
private static UUID WR_SERVICE = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
private static UUID READERID = UUID.fromString("0000fff4-0000-1000-8000-00805f9b34fb");
private static UUID WRITEID = UUID.fromString("0000fff3-0000-1000-8000-00805f9b34fb");
BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
mlog.e("已连接");
mConnectionState = STATE_CONNECTING;
mBluetoothGatt.discoverServices();//连上蓝牙就可以对服务进行查找发现
break;
case BluetoothProfile.STATE_DISCONNECTED:
mlog.e("未连接");
mConnectionState = STATE_DISCONNECT;
mBluetoothGatt.close();//关键 如果断开蓝牙时没有释放资源会导致 133 错误
break;
}
if (status == BluetoothGatt.GATT_FAILURE) {
mlog.e(status);
Toast.makeText(getBaseContext(), "onConnectionStateChange status: " + status, Toast.LENGTH_LONG).show();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//mlog.e(" 发现服务");
//打开读写服务
BluetoothGattService readerService = mBluetoothGatt.getService(WRITESERVICE);
//读的特征值
BluetoothGattCharacteristic reader = readerService.getCharacteristic(READERID);
//写的特征值
BluetoothGattCharacteristic writer = readerService.getCharacteristic(WRITEID);
for (BluetoothGattDescriptor descriptor : reader.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
mBluetoothGatt.setCharacteristicNotification(reader, true);//打开通知 才能接收到数据回调
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
mlog.e("onCharacteristicRead>>>>>>" + characteristic);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
//写入会有回调
//数据写入结果回调 characteristic为你写入的指令 这可以判断数据是否完整写入
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//这里接收到的就是蓝牙设备反馈回来的数据
for (int i = 0; i < characteristic.getValue().length; i++) {
Log.e("TAG", "------------获取数据 value[" + i + "]: " + characteristic.getValue()[i]);
}
}
//还有一些其他的回调基本用不上了
};
//置零
private byte[] buf_reset;
public void zeroOdert() {
//这里的 VERIFY是协议定义的指令校验 是把前面的数进行异或运算得到的值(具体要看协议怎么定义)
byte VERIFY = 0;
buf_reset = new byte[]{0x4C, 0x77, 0x06, 0x01, 0x72, VERIFY, 0x04, 0x1C};
for (int i = 0; i < 5; i++) {
VERIFY ^= buf_paramOder[i];
}
writer.setValue( buf_reset );
mBluetoothGatt.writeCharacteristic( writer );
}
//对应的反馈和读操作 以下是BluetoothGattCallback回调里的操作
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged( gatt, characteristic );
int reres = characteristic.getValue()[5];
if (reres == 0) {
//置零成功
} else if (reres == 1) {
//置零失败
}
}
ok基本操作就这些了
4、所有的蓝牙操作使用 Handler 固定在一条线程操作,这样能省去很多因为线程不同步导致的麻
5、如果无法扫描请检查是否给了相应的权限
人在年轻的时候,最头痛的是要解决这一生要做什么——by王小波