Android手机蓝牙总结之低功耗蓝牙

2018-05-13  本文已影响69人  Jarly_Sun

低功耗蓝牙也就是蓝牙4.0以后的版本,在android手机是4.3以后才支持。在项目中,开始使用了传统蓝牙去进行连接,发现失败了,始终没办法连接上。后来发现蓝牙硬件是4.0后的蓝牙版本,所以下面记录下android4.3版本后的蓝牙使用。

首先,需要明白一些专业的术语,GATT,characteristics,Properties,Descriptor,Service,UUID。

GATT

GATT是蓝牙连接后读写蓝牙数据的通用规范。它基于ATT通讯协议(Attribute Protocol),目的是在传输信息过程中使用尽量少的数据。在传输时,信息将以特征值(characteristics)和服务(services)的信息传输。这些信息都具有唯一的UUID。下面分别介绍一些基本概念。

characteristics

特征值。可以理解为一个数据类型。包括一个值(Value),一个属性(Property)和0至多个对值的描述(Descriptor)。

Properties

定义了characteristic的Value如何被使用,以及characteristic的Descriptor如何被访问。

Descriptor

是与Characteristic值相关的描述,例如范围、计量单位等。

Service

是Characteristic的集合。一个Service一般包含多个Characteristic。

UUID

唯一标示符,每个Service,Characteristic,Descriptor,都一个唯一的UUID。以后在对它们操作时,可以通过它们的UUID查找到对应的Service,Characteristic或者Descriptor

判断是否支持蓝牙

 public boolean checkIfSupportBle() {
  return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);}

开启蓝牙

这里有两种方式:

public void enableBluetooth(Activity activity) {
        if (bleAdapter == null || !bleAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }
    }
public void enableBluetooth() {
       if (bleAdapter == null || !bleAdapter.isEnabled()) {
         bleAdapter.enable();
      }
  }

扫描广播包

有两种方式获得BluetoothAdapter
第一种通过先获取BluetoothManager,然后获得BlueAdapter

private BluetoothManager bleManager;
private BluetoothAdapter bleAdapter;
bleManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bleManager.getAdapter();

第二种是直接获取BluetoothAdapter

BluetoothAdapter bleAdapter = BluetoothAdapter.getDefaultAdapter();

获取蓝牙匹配设备

public List<BluetoothDevice> getBoundDevices() {
        List<BluetoothDevice> devices = new ArrayList<>();
        Set<BluetoothDevice> boundDevices = bleAdapter.getBondedDevices();
        for (BluetoothDevice device : boundDevices) {
            //对device进行其他操作,比如连接等。
            devices.add(device);
        }
        return devices;
    }

开始扫描

public void startLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
        bleAdapter.startLeScan(leScanCallback);
    }
//加入蓝牙过滤
public void startLeScan(UUID[] uuids, BluetoothAdapter.LeScanCallback leScanCallback) {
        bleAdapter.startLeScan(uuids, leScanCallback);
    }

关闭蓝牙搜索

 public void stopLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
        bleAdapter.stopLeScan(leScanCallback);
    }

实现BluetoothAdapter.LeScanCallback接口

public class MyLeScanCallback implements BluetoothAdapter.LeScanCallback {
    private String mac;
    private Context mContext;
    private LeScanInterface leScanInterface;
    public MyLeScanCallback(Context mContext, String mac) {
        this.mac = mac;
        this.mContext = mContext;
    }

    public MyLeScanCallback(Context mContext) {
        this.mContext = mContext;
    }

    @Override
    public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
        String bleMac = bluetoothDevice.getAddress();
        if (TextUtils.equals(mac, bleMac)) {
            BleManager manager = BleManager.getInstance(mContext);
            manager.setDevice(bluetoothDevice);
        }
        if (leScanInterface != null) {
            leScanInterface.onMyLeScan(bluetoothDevice, i, bytes);
        }

    }

    public void setOnLeScan(LeScanInterface leScanInterface) {
        this.leScanInterface = leScanInterface;
    }

    /**
     * 蓝牙扫描回调接口
     */
    public interface LeScanInterface {
        /**
         * 蓝牙扫描回调
         * @param bluetoothDevice
         * @param i
         * @param bytes
         */
        void onMyLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes);
    }
}

Android5.0以后将蓝牙搜索封装成独立的对象,新的写法

BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); 
bluetoothLeScanner.startScan(mScanCallback);
private ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult results) {
            super.onScanResult(callbackType, results);
            BluetoothDevice device = results.getDevice();
            if (!devices.contains(device)) {  //判断是否已经添加
                devices.add(device);//也可以添加devices.getName()到列表,这里省略            }
            // callbackType:回调类型
            // result:扫描的结果,不包括传统蓝牙        }
    };

支持不同版本的写法

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        ...
        //定义的低于android5.0扫描方法
        startScan()
    } else {
        ...
        //定义的高于android5.0扫描方法
        startLeScan()
    }

建立蓝牙连接

/**
     * 连接蓝牙设备
     *
     * @param auto
     * @param mGattCallback
     * @return
     */
    public BluetoothGatt connectDevice(boolean auto, BluetoothGattCallback mGattCallback) {
        bleGatt = device.connectGatt(mContext, auto, mGattCallback);
        Log.e(TAG, "生成BluetoothGatt----->" + this.bleGatt);
        return bleGatt;
    }

获取device

/**
     * 通过mac地址直接得到BluetoothDevice
     *
     * @param mac
     * @return
     */
    public BluetoothDevice getRemoteDevice(String mac) {
        device = bleAdapter.getRemoteDevice(mac);
        return device;
    }

继承BluetoothGattCallback类,复写相关的方法

public class BleGattCallback extends BluetoothGattCallback {
    private static final String TAG = BleGattCallback.class.getName();
    private Context mContext;
    private CharacterInterface characterInterface;
    private BleStateInterface bleStateInterface;

    public BleGattCallback(Context mContext) {
        this.mContext = mContext;
    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.e(TAG, "成功建立蓝牙通道");
            if (newState == BluetoothProfile.STATE_CONNECTED) {
            //发现服务
                gatt.discoverServices();
                Log.e(TAG, "蓝牙连接成功!");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.e(TAG, "蓝牙连接断开!");
            }
            if (bleStateInterface != null) {
                bleStateInterface.onConnectedChange(newState);
            }
        } else {
            Log.e(TAG, "建立蓝牙通道失败");
            if (bleStateInterface != null) {
                bleStateInterface.onBleGattStatus(status);
            }

        }

    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            BleManager.getInstance(mContext).enableNotification(gatt, true);
        }
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic
            characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
        BleManager.getInstance(mContext).writeData();
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
            characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        if (characterInterface != null) {
            characterInterface.notifyChanged(gatt, characteristic);
        }
    }


    public void setOnCharacterInterface(CharacterInterface characterInterface) {
        this.characterInterface = characterInterface;
    }

    public void setOnConnectedChange(BleStateInterface bleStateInterface) {
        this.bleStateInterface = bleStateInterface;
    }

    public interface CharacterInterface {
        /**
         * 特征值改变通知
         *
         * @param gatt
         * @param characteristic
         */
        void notifyChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
    }

    public interface BleStateInterface {
        /**
         * 蓝牙连接状态,注意回调方法不在主线程
         *
         * @param newState
         */
        void onConnectedChange(int newState);

        /**
         * 蓝牙通道建立状态回调,失败的话需要关闭BlueToothGatt和BlueToothService,然后重新调用蓝牙重连
         *
         * @param status
         */
        void onBleGattStatus(int status);


    }
}

封装成完整的类

public class BleManager {
    private BluetoothAdapter bleAdapter;
    private BluetoothManager bleManager;
    private BluetoothGatt bleGatt;
    private BluetoothGattService gattService;
    private BluetoothDevice device;
    private Context mContext;
    private static volatile BleManager singleton;
    private final int REQUEST_ENABLE_BT = 1;
    private StringBuffer buffer = new StringBuffer();
    private String TAG = getClass().getName();

    private BleManager(Context mContext) {
        this.mContext = mContext;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            bleManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
            bleAdapter = bleManager.getAdapter();
        } else {
            bleAdapter = BluetoothAdapter.getDefaultAdapter();
        }
    }

    /**
     * 获取蓝牙管理类的单例
     *
     * @param context
     * @return
     */
    public static BleManager getInstance(Context context) {
        if (singleton == null) {
            synchronized (BleManager.class) {
                if (singleton == null) {
                    singleton = new BleManager(context);
                }
            }
        }
        return singleton;
    }

    /**
     * 判断是否支持蓝牙设备
     *
     * @return
     */
    public boolean checkIfSupportBle() {
        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }

    /**
     * 开启蓝牙设备
     */
    public void enableBluetooth(Activity activity) {
        if (bleAdapter == null || !bleAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }
    }


    public void enableBluetooth() {
        if (bleAdapter == null || !bleAdapter.isEnabled()) {
            bleAdapter.enable();
        }
    }


    /**
     * 获取蓝牙绑定了的列表设备
     */
    public List<BluetoothDevice> getBoundDevices() {
        List<BluetoothDevice> devices = new ArrayList<>();
        Set<BluetoothDevice> boundDevices = bleAdapter.getBondedDevices();
        for (BluetoothDevice device : boundDevices) {
            //对device进行其他操作,比如连接等。
            devices.add(device);
        }
        return devices;
    }

    /**
     * 通用蓝牙扫描方法,持续时间大概10s,扫描到蓝牙发出广播进行接收
     */
    public void startDiscover() {
        bleAdapter.startDiscovery();
    }

    /**
     * 该方法目前稳定
     *
     * @param leScanCallback
     */
    public void startLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
        bleAdapter.startLeScan(leScanCallback);
    }

    /**
     * 通过UUID过滤筛选出合适的蓝牙设备
     *
     * @param uuids
     * @param leScanCallback
     */
    public void startLeScan(UUID[] uuids, BluetoothAdapter.LeScanCallback leScanCallback) {
        bleAdapter.startLeScan(uuids, leScanCallback);
    }

    /**
     * 代替果实的startLeScan(),但是不稳定
     *
     * @param scanCallback
     */
    @RequiresApi (api = Build.VERSION_CODES.LOLLIPOP)
    public void startBleScan(ScanCallback scanCallback) {
        bleAdapter.getBluetoothLeScanner().startScan(scanCallback);
    }

    /**
     * 关闭蓝牙搜索
     *
     * @param leScanCallback
     */
    public void stopLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
        bleAdapter.stopLeScan(leScanCallback);
    }

    /**
     * 蓝牙是否开启
     *
     * @return
     */
    public boolean isOpen() {
        return bleAdapter.isEnabled();
    }

    /**
     * 关闭蓝牙设备
     *
     * @param scanCallback
     */
    @RequiresApi (api = Build.VERSION_CODES.LOLLIPOP)
    public void stopBleScan(ScanCallback scanCallback) {
        bleAdapter.getBluetoothLeScanner().stopScan(scanCallback);
    }

    public void setBleGatt(BluetoothGatt bleGatt) {
        this.bleGatt = bleGatt;
    }

    public void setGattService(BluetoothGattService gattService) {
        this.gattService = gattService;
    }

    public BluetoothGatt getBleGatt() {
        return bleGatt;
    }

    public BluetoothGattService getGattService() {
        return gattService;
    }

    public BluetoothDevice getDevice() {
        return device;
    }

    public void setDevice(BluetoothDevice device) {
        this.device = device;
    }

    /**
     * 开启蓝牙设备
     */
    public void openBle() {
        bleAdapter.enable();
    }

    /**
     * 关闭蓝牙设备
     */
    public void closeBle() {
        bleAdapter.disable();
    }

    /**
     * 通过mac地址直接得到BluetoothDevice
     *
     * @param mac
     * @return
     */
    public BluetoothDevice getRemoteDevice(String mac) {
        device = bleAdapter.getRemoteDevice(mac);
        return device;
    }

    /**
     * 连接蓝牙设备
     *
     * @param auto
     * @param mGattCallback
     * @return
     */
    public BluetoothGatt connectDevice(boolean auto, BluetoothGattCallback mGattCallback) {
        bleGatt = device.connectGatt(mContext, auto, mGattCallback);
        Log.e(TAG, "生成BluetoothGatt----->" + this.bleGatt);
        return bleGatt;
    }

    /**
     * 获取对应应用的服务
     * @return
     */
    public BluetoothGattService getDefaultGattService() {
        this.gattService = bleGatt.getService(UUID.fromString(UUIDManager.SERVICE_UUID));
        return gattService;
    }


    @SuppressLint ("NewApi")
    public boolean enableNotification(BluetoothGatt bluetoothGatt, boolean enable) {
        if (gattService == null) {
            getDefaultGattService();
        }
        BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(UUIDManager.NOTIFY_UUID));
        if (bluetoothGatt == null || characteristic == null) {
            return false;
        }
        if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
            return false;
        }
        //获取到Notify当中的Descriptor通道  然后再进行注册
        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(UUIDManager.NOTIFY_DESCRIPTOR));
        if (clientConfig == null) {
            return false;
        }
        if (enable) {
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return bluetoothGatt.writeDescriptor(clientConfig);
    }

    /**
     * 蓝牙设备传数据
     *
     * @param data
     * @return
     */
    private boolean writeBluetoothData(String data) {
        BluetoothGattCharacteristic writeCharacter = null;
        if (gattService == null) {
            gattService = getDefaultGattService();
            if (gattService == null) {
                return false;
            }
        }
        writeCharacter = gattService.getCharacteristic(UUID.fromString(UUIDManager.WRITE_UUID));
        // 设置监听
        this.bleGatt.setCharacteristicNotification(writeCharacter, true);
        // 当数据传递到蓝牙之后
        // 会回调BluetoothGattCallback里面的write方法
        writeCharacter.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        // 将需要传递的数据 打碎成16进制
        try {
            writeCharacter.setValue(CommonUtils.getHexBytes(data));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return this.bleGatt.writeCharacteristic(writeCharacter);
    }

    /**
     * 向蓝牙设备写入数据
     */
    public void writeData(String content) {
        buffer.append(content);
        writeData();
    }

    public void writeData() {
        int length = buffer.length();
        String writeData = "";
        if (length >= 20) {
            writeData = buffer.substring(0, 20);
        } else {
            writeData = buffer.toString();
        }
        if (writeData.length() > 0) {
            writeBluetoothData(writeData);
        }
        if (writeData.length() > 0) {
            buffer.delete(0, writeData.length());
        }
    }


    /**
     * 关闭蓝牙连接
     */
    public void closeGatt() {
        if (bleGatt != null) {
            bleGatt.disconnect();
            bleGatt.close();
            Log.e(TAG, "连接蓝牙断开bleGatt" + this.bleGatt);
            Log.e(TAG, "连接蓝牙断开gattService" + this.gattService);
            bleGatt = null;
            gattService = null;
        }
    }
}

UUID的获取

一般而言UUID会由硬件工程师提供的,但是有时候硬件工程师也不知道,这时就需要自己去检测了。首先通过寻找服务Service的UUID,然后通过找出特征值的UUID,并且判断特征值的UUID的属性是读,写还是通知。也可以可以借助引用nRF Connect连接蓝牙得到相应的值。

/**
     * 展示服务Services和characteristic对应的UUID,以及具备的属性。
     *
     * @param gattServices
     */
    public boolean queryGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) {
            return false;
        }
        for (BluetoothGattService myService : gattServices) {
        //找出服务的UUID
            String uuId = myService.getUuid().toString();
              List<BluetoothGattCharacteristic> gattCharacteristics =myService.getCharacteristics();  
            for (final BluetoothGattCharacteristic  gattCharacteristic: gattCharacteristics) {  
                Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid());  

                int charaProp = gattCharacteristic.getProperties();  
               //所有Characteristics按属性分类
               if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                     Log.d(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid());
                     Log.d(TAG, "gattCharacteristic的属性为:  可读");
                    }
               if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
                            Log.d(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid());
                            Log.d(TAG, "gattCharacteristic的属性为:  可写");
                        }
               if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                            Log.d(TAG, "gattCharacteristic的UUID为:" + gattCharacteristic.getUuid() + gattCharacteristic);
                            Log.d(TAG, "gattCharacteristic的属性为:  具备通知属性");
                        }
                   }
               return false;
        }
    }

这样就能找到对应服务的UUID和特征值characteristic的UUID,从而进行相应的读写和通知的操作。读写通知有可能公用一个通道,即特征值UUID有可能相同,但是这不影响通信。对于外部蓝牙硬件,一般通过通知的方式,接收返回的数据,开启蓝牙通知的方式为BleManager的enableNotification()方法。
补充蓝牙读的写法:

bleGatt.readCharacteristic(characteristic);

如果要使用,放在BluetoothGattCallback复写方法onCharacteristicRead()方法下进行逻辑操作。

上一篇下一篇

猜你喜欢

热点阅读