美文网首页
Android手机蓝牙总结之低功耗蓝牙

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

作者: Jarly_Sun | 来源:发表于2018-05-13 21:39 被阅读69次

    低功耗蓝牙也就是蓝牙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()方法下进行逻辑操作。

    相关文章

      网友评论

          本文标题:Android手机蓝牙总结之低功耗蓝牙

          本文链接:https://www.haomeiwen.com/subject/cdnfdftx.html