一、BLE概述
Android4.3以上加入了BLE——低功耗蓝牙。
使用低功耗蓝牙可以包括多个Profile,一个Profile中有多个Service,一个Service中有多个Characteristic,一个Characteristic中包括一个value和多个Descriptor。
二、BLE的使用
1.权限
和经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限:
除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:
按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature:
// 判断当前手机是否支持BLE
if(!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Message msg = Message.obtain();
msg.what=MSG_WHAT_NO_SUPPORT_BLE;
handler.sendMessage(msg);
return false;
}
2.使用步骤
2.1 初始化 BluetoothAdapter,用来判断设备是否支持蓝牙并打开蓝牙
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
finalBluetoothManager bluetoothManager =
(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter= bluetoothManager.getAdapter();
// 判断是否支持蓝牙
if(mBluetoothAdapter==null) {
Message msg = Message.obtain();
msg.what=MSG_WHAT_NO_SUPPORT_BL;
handler.sendMessage(msg);
return false;
}
//打开蓝牙
if(!mBluetoothAdapter.isEnabled()) {
ILog.e("蓝牙未打开");
Message msg = Message.obtain();
msg.what=MSG_WHAT_OPEN_BL;
handler.sendMessage(msg);
return false;
}
2.2 扫描蓝牙设备
扫描指定UUID的设备,mLeScanCallback为扫描的回调接口
mBluetoothAdapter.startLeScan(UUID[],mLeScanCallback);
//回调接口
BluetoothAdapter.LeScanCallbackmLeScanCallback=newBluetoothAdapter.LeScanCallback() {
// 开始扫描和停止扫描都会调用此方法
@Override
public voidonLeScan(finalBluetoothDevice device,intrssi,byte[] scanRecord) {
//可通过对比当前扫描到的device的name与自己想查找到的设备name对比,来获取想连接的device信息
}
2.3 停止扫描
可以在扫描到自己需要的蓝牙后,停止扫描,注意:停止扫描和开始扫描的回调接口是同一个
mBluetoothAdapter.stopLeScan(mLeScanCallback);
2.4 连接设备
if(mBluetoothAdapter!=null) {
// 获取BluetoothGatt,连接设备,设为自动连接(即第二个参数为true),可能会存在问题,
mBluetoothGatt= device.connectGatt(context,false,BluetoothGattCallback callback);
//连接后的回调接口
BluetoothGattCallback callback=new BluetoothGattCallback() {
// 若连接状态发生改变
@Override
public void onConnectionStateChange(BluetoothGatt gatt,intstatus,intnewState) {
// status 表示相应的连接或断开操作是否完成,而不是指连接状态
if(newState == BluetoothProfile.STATE_CONNECTED) {
ILog.e("Connected to GATT server.");
}else if(newState == BluetoothProfile.STATE_DISCONNECTED) {
ILog.e("Disconnected from GATT server.");
}
@Override
public voidonServicesDiscovered(BluetoothGatt gatt,intstatus) {
Message msg = Message.obtain();
if(status == BluetoothGatt.GATT_SUCCESS) {
ILog.e("扫描到服务");
// 获取我们需要的服务
BluetoothGattService service =mBluetoothGatt.getService(SERVICE_UUID);
// 我需要得到的应该是一个特定的BluetoothGattCharacteristic,根据uuid获取
BluetoothGattCharacteristic mCharacteristic = service.getCharacteristic(CHARAC_READ_UUID);
}
// 接收到信息
@Override
public voidonCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
readInfo(characteristic);
}
//信息写入成功回调
@Override
public voidonCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,intstatus) {
if(status == BluetoothGatt.GATT_SUCCESS) {
ILog.e("回调:信息写入成功");
}else{
ILog.e("回调:信息写入失败");
}
2.5 连接到设备后的处理
1.获取指定服务及特征值:在连接设备的回调接口中有扫描到服务的方法,可以在此方法中获取指定的BluetoothGattService,并且可以在该服务中获取指定的BluetoothGattCharacteristic,如果需要开启notify功能(此功能可以不用主动轮询消息,而是被动接收设备发来消息)
// 给设备设置notifity功能
// 如果设备主动给手机发信息,则可以通过notification的方式,这种方式不用手机去轮询地读设备上的数据
mBluetoothGatt.setCharacteristicNotification(mCharacteristic,true);
BluetoothGattDescriptor descripter = mCharacteristic.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
if(descripter !=null) {
descripter.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descripter);
}
2.读取消息:由于BLE是低功耗的,每次可传输的字节数为20,所以读取消息需要进行拼接,这就需要发送的内容加前后缀,来判断是否拼接成了完整的数据
3.发送消息:确定扫描到服务之后才能进行数据发送,并且需要分包发送,我是开启线程,在线程中进行数据的分包,将分包的数据加入队列中,并轮询队列,有数据就发送
/**
* BLE数据发送对象
*/
public class BleSenderextendsThread {
public static final intMAX_LENGTH=18;
private BlockingQueuedatas;
private BluetoothGattmBluetoothGatt;
private booleanisRunning;
private booleanisExit;
publicBleSender() {
isRunning=false;
}
public void init(BluetoothGatt mBluetoothGatt) {
clearAllData();
datas=new ArrayBlockingQueue(100);
this.mBluetoothGatt= mBluetoothGatt;
isExit=false;
if(!isRunning) {
start();
}
}
public void addData(BluetoothGattCharacteristic characteristic, String data) {
if(TextUtils.isEmpty(data))
return;
//分包
byte[] bytes = data.getBytes();
intlength = bytes.length;
intnumber = length %MAX_LENGTH==0? length /MAX_LENGTH: length /MAX_LENGTH+1;
for(inti =0; i < number; i++) {
byte[] range = Arrays.copyOfRange(bytes, i *18, i == number -1? length : (i +1) *MAX_LENGTH);
try{
// 将分包后的每个包的信息都填加到队列中
datas.put(newBLEData(characteristic, range));
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
private void sendData(BLEData data) {
BluetoothGattCharacteristic characteristic = data.characteristic;
byte[] bytes = data.data;
if(characteristic ==null|| bytes ==null)
return;
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
characteristic.setValue(bytes);
mBluetoothGatt.writeCharacteristic(characteristic);
}
public void clearAllData() {
if(datas!=null)
datas.clear();
}
@Override
public void run() {
super.run();
isRunning=true;
while(!isExit) {
try{
BLEData data =datas.take();
Thread.sleep(20);
sendData(data);
}catch(Exception e) {
e.printStackTrace();
}
public void stopSend() {
isExit=true;
}