package com.nobo.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.blankj.utilcode.util.LogUtils;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.UUID;
/**
* Created by MISHAOSHUAI
* on 2021/8/17.
*/
public class BLEConnect
{
private static BluetoothGattCharacteristic mNotifyCharacteristic;
private static com.nobo.bluetooth.BtBase.Listener mListener;
private String serviceUuid = "";
private String characterUuid = "";
private static final String TAG = "BLEConnect";
private static BluetoothGatt mBluetoothGatt;
private boolean isConnected;
private Handler mDiscoverServiceHandler = new Handler();
private Runnable mDisvoverServiceRunnable = new Runnable()
{
@Override
public void run()
{
LogUtils.d("重新获取服务");
disConn();
connect(mBTmodel);
}
};
private BTmodel mBTmodel;
private static BLEConnect sInstance;
private BLEConnect()
{
}
public static BLEConnect getInstance(BtBase.Listener listener)
{
if (sInstance == null)
{
synchronized (BLEConnect.class)
{
if (sInstance == null)
{
sInstance = new BLEConnect();
}
}
}
setListener(listener);
return sInstance;
}
// 与服务端连接的Callback
public BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback()
{
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
BluetoothDevice dev = gatt.getDevice();
Log.i(TAG, String.format("onConnectionStateChange:%s,%s,%s,%s", dev.getName(), dev.getAddress(), status, newState));
if (status == BluetoothGatt.GATT_SUCCESS &&
newState == BluetoothProfile.STATE_CONNECTED)
{
isConnected = true;
gatt.discoverServices(); //启动服务发现
mDiscoverServiceHandler.postDelayed(mDisvoverServiceRunnable, 2000);
}
else
{
isConnected = false;
disConn();
notifyUI(BtBase.Listener.DISCONNECTED, mBTmodel);
LogUtils.d("连接失败");
}
LogUtils.d(String.format(
status == 0 ? (newState == 2 ? "与[%s]连接成功" : "与[%s]连接断开") : ("与[%s]连接出错,错误码:" +
status), dev));
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
mDiscoverServiceHandler.removeCallbacksAndMessages(null);
if (status == BluetoothGatt.GATT_SUCCESS)
{
LogUtils.d("发现服务成功,连接成功");
notifyUI(BtBase.Listener.CONNECTED, mBTmodel);
//BLE服务发现成功
// 遍历获取BLE服务Services/Characteristics/Descriptors的全部UUID
for (BluetoothGattService service : gatt.getServices())
{
StringBuilder allUUIDs = new StringBuilder(
"UUIDs={\nS=" + service.getUuid().toString());
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics())
{
allUUIDs.append(",\nC=").append(characteristic.getUuid());
for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors())
{
allUUIDs.append(",\nD=").append(descriptor.getUuid());
}
}
allUUIDs.append("}");
LogUtils.d("发现服务" + allUUIDs);
}
//BLE服务发现成功
serviceUuid = "your service uuid";
characterUuid = "your character uuid";
BluetoothGattService service = gatt.getService(UUID.fromString(serviceUuid));
mNotifyCharacteristic = service.getCharacteristic(UUID.fromString(characterUuid));
if (mNotifyCharacteristic != null)
{
//使能Notify
setCharacteristicNotification(mNotifyCharacteristic, true);
}
// read();
}
else
{
LogUtils.d("发现服务失败,连接失败");
disConn();
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
UUID uuid = characteristic.getUuid();
String valueStr = new String(characteristic.getValue());
notifyUI(BtBase.Listener.READ, valueStr);
LogUtils.d("读取Characteristic[" + uuid + "]:\n" + valueStr);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
UUID uuid = characteristic.getUuid();
String valueStr = new String(characteristic.getValue());
notifyUI(BtBase.Listener.MSG, valueStr);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
UUID uuid = characteristic.getUuid();
String valueStr = new String(characteristic.getValue());
LogUtils.d("通知Characteristic[" + uuid + "]:\n" + valueStr);
notifyUI(BtBase.Listener.READ, valueStr);
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
UUID uuid = descriptor.getUuid();
String valueStr = Arrays.toString(descriptor.getValue());
LogUtils.d("读取Descriptor[" + uuid + "]:\n" + valueStr);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
UUID uuid = descriptor.getUuid();
String valueStr = Arrays.toString(descriptor.getValue());
LogUtils.d("写入Descriptor[" + uuid + "]:\n" + valueStr);
//询问状态
write("A");
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
{
super.onReadRemoteRssi(gatt, rssi, status);
LogUtils.d("RSSI:" + rssi);
}
};
/**
* BLE中心设备连接外围设备的数量有限(大概2~7个),
* 在建立新连接之前必须释放旧连接资源,否则容易出现连接错误133
*/
public void disConn()
{
if (mBluetoothGatt != null)
{
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
mBluetoothGatt = null;
mNotifyCharacteristic = null;
}
}
// 注意:连续频繁读写数据容易失败,读写操作间隔最好200ms以上,或等待上次回调完成后再进行下次读写操作!
// 读取数据成功会回调->onCharacteristicChanged()
public void read()
{
BluetoothGattService service = getGattService(UUID.fromString(serviceUuid));
if (service != null)
{
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characterUuid));//通过UUID获取可读的Characteristic
mBluetoothGatt.readCharacteristic(characteristic);
}
}
// 注意:连续频繁读写数据容易失败,读写操作间隔最好200ms以上,或等待上次回调完成后再进行下次读写操作!
// 写入数据成功会回调->onCharacteristicWrite()
public void write(String data)
{
BluetoothGattService service = getGattService(UUID.fromString(serviceUuid));
if (service != null)
{
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characterUuid));//通过UUID获取可写的Characteristic
characteristic.setValue(data.getBytes(StandardCharsets.UTF_8)); //单次最多20个字节
mBluetoothGatt.writeCharacteristic(characteristic);
}
}
// 获取Gatt服务
private BluetoothGattService getGattService(UUID uuid)
{
if (!isConnected)
{
ToastyUtils.toastWarn("没有连接", true);
return null;
}
BluetoothGattService service = mBluetoothGatt.getService(uuid);
if (service == null)
{
ToastyUtils.toastWarn(
NoboApplication.getContext().getString(R.string.cant_find_service_uuid) +
uuid, true);
}
return service;
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void connect(BTmodel dev)
{
LogUtils.d("BLE connect");
mBTmodel = dev;
mBluetoothGatt = dev.getDevice()
.connectGatt(NoboApplication.getContext(),
false,
mBluetoothGattCallback,
2,
BluetoothDevice.PHY_LE_1M_MASK); // 连接蓝牙设备
}
public void notifyUI(final int state, final BTmodel obj)
{
NoboApplication.runUi(() -> {
try
{
if (mListener != null)
{
mListener.socketNotify(state, obj);
}
else
{
LogUtils.d("mListener == null");
}
}
catch (Throwable e)
{
LogUtils.d("回调异常");
e.printStackTrace();
}
});
}
public void notifyUI(final int state, final String obj)
{
NoboApplication.runUi(() -> {
try
{
if (mListener != null)
{
mListener.socketNotify(state, obj);
}
else
{
LogUtils.d("mListener == null");
}
}
catch (Throwable e)
{
LogUtils.d("回调异常");
e.printStackTrace();
}
});
}
/**
* 释放监听引用(例如释放对Activity引用,避免内存泄漏)
*/
public void unListener()
{
mListener = null;
LogUtils.d("mListener置空");
}
public boolean isConnected()
{
return isConnected;
}
public void setConnected(boolean connected)
{
isConnected = connected;
}
public BtBase.Listener getListener()
{
return mListener;
}
public static void setListener(BtBase.Listener listener)
{
mListener = listener;
}
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled)
{
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to BLE SPP Notify.
// if (characterUuid.equals(characteristic.getUuid()))
{
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
}
要交互,就要像上面那样setCharacteristicNotification才能收到消息