Android Ble蓝牙开发

1.权限设置

    <uses-feature android:name="android.hardware.location.gps" />
    <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.ACCESS_FINE_LOCATION" />

这里有人可能疑惑,使用蓝牙为啥要定位权限,其实蓝牙技术是可以实现定位的,要使用蓝牙必须要申请定位权限,android 9 之后动态权限申请

private void checkPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int fine_location = checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION);
            int cross_location = checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION);
            if (fine_location != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, 1);
            } else {
                finish();
                return;
            }
            if (cross_location != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
            } else {
                finish();
                return;
            }
        }
    }

动态申请,提示的是申请定位权限,这点个不要奇怪。

2.获取蓝牙设备管理器

private void getBleAdapter() {
        if (mAdapter != null) {
            return;
        }
        BluetoothManager bluetoothManager =
                (BluetoothManager) context.getSystemService (Context.BLUETOOTH_SERVICE);
        mAdapter = bluetoothManager.getAdapter ();
    }

这里获取到的是一个蓝牙适配器BluetoothAdapter ,后续通过适配器调用蓝牙适配器。

3.设备搜索

mAdapter.startLeScan (leScanCallback);
BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback () {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
         //device.getName()  设备名称
         //device.getAddress()  mac地址
        }
    };

这个搜索方法在21 已经过期了后续使用

 mAdapter.getBluetoothLeScanner ().startScan (new ScanCallback () {
                    @Override
                    public void onScanResult(int callbackType, ScanResult result) {
                        super.onScanResult (callbackType, result);
                     //    BluetoothDevice device= result.getDevice ();  获取设备对象
                     //   result.getRssi ()  信号强度,根据这个可以计算出距离服务端蓝牙的距离
                    }
                });

3.1 停止搜索

mAdapter.stopLeScan (leScanCallback);

同样这个方法21后过期了,可以使用新的api

mAdapter.getBluetoothLeScanner ().stopScan(// 搜索接口实例);

在搜到合适设备或者想停止设备搜索时可以调用这个api停止搜索。

  • 注意:如果去连接设备时,同时还在搜索设备,建议先调用stopScan 停止搜索,然后再连接,这样可以提高效率。

4.设备连接

一般情况下,根据业务不同有可能我们会将搜索到的BluetoothDevice 保存下来。
再次使用时可以用

BluetoothDevice device = mAdapter.getRemoteDevice (deviceInfo.getAddress ());

如果device 是空的,说明设备可能已经关闭了,后边就没必要链接了。
如果是直接搜索到连接可以直接用搜到的BluetoothDevice 直接连接。

BluetoothGatt  currentBindGatt = device.connectGatt (context, true, bluetoothGattCallback);
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
            BluetoothGattCallback callback) {
        return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO));
    }
  • autoConnect 是否重连,如果true 异常断开后下次还会重新连接。
  • bluetoothGattCallback 连接回掉状态
   BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback () {
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        // 这个是发现服务,在这里可以注册蓝牙的通知
            LogS.d (TAG, "onServicesDiscovered()");
            if (gatt == null || gatt.getDevice () == null) {
                LogS.d (TAG, "Android GATT invalid!");
                return;
            }
            UUID uuid = BeanBleUtil.parseUUID (BeanCSConstant.PROFILE_FORMAT_SERVER_UUID);
    
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //查询支持的Server uuid
                List<BluetoothGattService> services = gatt.getServices ();
                if (services != null) {
                    for (BluetoothGattService service : services) {
                        LogS.d (TAG, "onServicesDiscovered:" + service.getUuid ());
                    }
                }
                //查询支持的Characteristic uuid
                BluetoothGattService service = gatt.getService (uuid);
                if (service == null) {
                    LogS.d (TAG, "BluetoothGattService == null");
                    return;
                }
                List<BluetoothGattCharacteristic> list = service.getCharacteristics ();
                if (list != null) {
                    for (BluetoothGattCharacteristic bluetoothGattCharacteristic : list) {
                    //这里将需要监听的通知进行注册,如果不需要监听,可以不用管这里
                        List<UUID> listUUids = NoticeUUidList ();
                        if (bluetoothGattCharacteristic != null && listUUids.contains (bluetoothGattCharacteristic.getUuid ())) {
                            LogS.d (TAG, "readData support uuid:" + bluetoothGattCharacteristic.getUuid ().toString ());
                            enableNotification (true, gatt, bluetoothGattCharacteristic);
                        }
                    }
                }
            }
        }

        @Override
        public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
            LogS.d (TAG, "onConnectionStateChange " + status + " ===> " + newState);
            if (gatt == null) {
                LogS.d (TAG, "gatt handler null");
                return;
            }
            BaseDeviceInfo cInfo = deviceHashMap.get (gatt.getDevice ().getAddress ());
            if (cInfo != null) {
                LogS.d (TAG, "onConnectionStateChange 链接成功,开始赋值对象:" + gatt.getDevice ().getAddress ());
            }
            if (status != BluetoothGatt.GATT_SUCCESS) {
                gatt.disconnect ();
                gatt.close ();
                LogS.d (TAG, "--GATT_SUCCESS");
                   currentBindGatt = null;      
                    LogS.d (TAG, "GATT_SUCCESS 连接失败操作");
                    return;
                }
            }

            if (newState == BluetoothGatt.STATE_CONNECTED) {
              //连接成功
            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
              //链接失败
            }
        }

        //读取
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        //数据的读取结果或者通知会通过这个函数返回
            LogS.d (TAG, "onCharacteristicRead:" + gatt.getDevice ().getName () + " address:" + gatt.getDevice ().getAddress () + " status:" + status + "characteristic.getUuid ():" + characteristic.getUuid ());
            if (status == BluetoothGatt.GATT_SUCCESS) {
                byte[] value = characteristic.getValue ();
                if (value != null) {
                    StringBuilder builder = new StringBuilder ();
                    for (byte data : value) {
                        //以十六进制的形式输出
                        LogS.d (TAG, "---------" + String.format ("%02X ", data));
                        builder.append (data);
                    }
                    LogS.d (TAG, "onCharacteristicRead:(uuid, value)= " + characteristic.getUuid ()
                            + "," + builder.toString () + ")");
                    UUID uuid = characteristic.getUuid ();
                    if (uuid == null) {
                        return;
                    }
                  //解析 收到的value 值
                }
            }
        }

        //写入
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            LogS.d (TAG, "onCharacteristicWrite:" + gatt.getDevice ().getName () + " address:" + gatt.getDevice ().getAddress () + " status:" + status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                byte[] value = characteristic.getValue ();
                if (value != null) {
                    StringBuilder builder = new StringBuilder ();
                    for (byte data : value) {
                        builder.append (data);
                    }
                    LogS.d (TAG, "onCharacteristicWrite:" + builder.toString ());
                }
            } else if (status == BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH) {
                LogS.d (TAG, "写入操作超出了属性的最大长度");
            }
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged (gatt, mtu, status);
            LogS.d (TAG, "----onMtuChanged");
        }

        //数据变化通知 接收通知消息
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            LogS.d (TAG, "通知回调 onCharacteristicChanged:" + gatt.getDevice ().getName () + " address:" + gatt.getDevice ().getAddress () + "通知uuid:" + characteristic.getUuid ().toString ());
            byte[] value = characteristic.getValue ();
            if (value != null) {
                StringBuilder builder = new StringBuilder ();
                for (byte data : value) {
                    builder.append (data);
                }
                LogS.d (TAG, "接收通知消息:" + builder.toString ());
               //处理通知消息
            }

        }

    };

5.设备的重连

在很多时候,如果设备已经连接过了,但是中间断开,再次需要链接的时候是不需要再次调用connectGatt 连接方法的。只要调用重连方法就可以了。
重连的前提是之前已经连接过,获取到了BluetoothGatt 实例对象。

 if (currentBindGatt.connect ()) {
 //调用重连成功,即使设备不在范围内,等设备在范围内可自动连接成功
 }

6.设备的断开与服务关闭

在需要断开设备时可以调用

currentBindGatt.disconnect ();

调用断开后,后续需要链接时,只需重新调用重连方法即可。
如果需要关闭Gatt 服务可以调用

 currentBindGatt.close ();

调用close 方法后就不能再调用重连方法了,必须重新调用connectGatt ,

  • 在实际的项目中可能会出现重连失败等操作,或者连接失败,这时需要调用下close方法,然后重新connectGatt 。

7.通知的注册与接收

ble支持通知类型的事件,当然,想要收到通知必须提前注册通知的uuid,有些uuid 是蓝牙的标准uuid ,有些则是厂商自定义的uuid .这个要适情况而定。

在Gatt 监听器的 onServicesDiscovered(BluetoothGatt gatt, int status)回掉函数中来注册。

  • 查询支持的Server uuid
    List services = gatt.getServices ();
    这里获取到设备所有的uuid 特服务
  • 查询支持的Characteristic uuid
    BluetoothGattService service = gatt.getService (uuid);
    这里的uuid 用的是181c 一般厂商都会给的,是服务端的uuid 。注意这个uuid 是UUID 类
    需要通过:
    String code = “0000” + 181c+ “-0000-1000-8000-00805f9b34fb”;
    UUID uuid=UUID.fromString(code);
    转为标准的UUID 类。
  • 获取这个服务下的所有特征列表
    List list = service.getCharacteristics ();
    然后比对下是否是我们需要注册的那个uuid 的特征值。
 if (list != null) {
                    for (BluetoothGattCharacteristic bluetoothGattCharacteristic : list) {
                        List<UUID> listUUids =NoticeUUidList ();
                        if (bluetoothGattCharacteristic != null && listUUids.contains (bluetoothGattCharacteristic.getUuid ())) {
                            LogS.d (TAG, "readData support uuid:" + bluetoothGattCharacteristic.getUuid ().toString ());
                            enableNotification (true, gatt, bluetoothGattCharacteristic);
                        }
                    }
                }

NoticeUUidList 是一个UUID 的list 是我想要监听的通知的uuid ,然后通过BluetoothGattCharacteristic
特征值来注册通知

 /**
     * 设置是否开启通知
     */
    private boolean enableNotification(boolean enable, BluetoothGatt
            gatt, BluetoothGattCharacteristic characteristic) {
        if (gatt == null || characteristic == null) return false; 
        boolean setNotification = false;
        try {
            String uuid =“2902”;
            BluetoothGattDescriptor characteristicDescriptor = characteristic.getDescriptor (BeanBleUtil.parseUUID (uuid));
            if (characteristicDescriptor != null) {
                byte[] value = enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
                characteristicDescriptor.setValue (value);
                gatt.writeDescriptor (characteristicDescriptor);
            }
            setNotification = gatt.setCharacteristicNotification (characteristic, enable);
            LogS.d (TAG, "设置通知" + characteristic.getUuid ().toString () + "结果:" + setNotification);
        } catch (Exception e) {
            LogS.d (TAG, "enableNotification 统一设置通知 失败" + e.getMessage () + "--uuid:" + characteristic.getUuid ().toString ());
            e.printStackTrace ();
        }
        return setNotification;
    }

2902 是蓝牙特征值的配置uuid ,如果没有配置这个uuid ,设置通知是没有效果的。
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE 是用于启动通知的描述符,必须配置。
然后就是通过 gatt.setCharacteristicNotification 来设置通知的特征值,设置成功后,服务端数据变化发起的通知,客户端就可以收到通知了,通知的接收是 onCharacteristicChanged函数

public static UUID parseUUID(String name) {
        if (TextUtils.isEmpty(name)) {
            return null;
        }
        String code = "0000" + name + "-0000-1000-8000-00805f9b34fb";
        if (!TextUtils.isEmpty(code)) {
            return UUID.fromString(code);
        }
        return null;
    }

8.数据的主动读取

通过蓝牙可以读取设备服务端的值,但是必须通过uuid 来完成。

public boolean readDataByUuid(String uuid) {
        LogS.d (TAG, "开始读取数据:readData() uuid: " + uuid);
        boolean ret = false;
        LogS.d (TAG, "readData:" + BeanBleUtil.parseUUID (uuid).toString ());
        if (currentBindGatt != null) {
            BluetoothGattService bleService = currentBindGatt.getService (BeanBleUtil.parseUUID (“181c));
            if (bleService == null) {
                Log.e (TAG, "readDataByUuid:  BluetoothGattService==null");
                return false;
            }
            BluetoothGattCharacteristic characteristic = bleService.getCharacteristic (BeanBleUtil.parseUUID (uuid));
            if (characteristic != null) {
                ret = currentBindGatt.readCharacteristic (characteristic);
            }
            LogS.d (TAG, "readData(" + uuid + ") ret: " + ret);
        } else {
            LogS.d (TAG, "readData currentBindGatt == null!");
        }
        return ret;
    }

读取完成后,数据是通过onCharacteristicRead 函数回掉回来。

9.数据的写入

写入和读取类似,都是先获取gatt服务,然后通过服务和uuid 获取特征值,然后写入数据。

public boolean writeByte(byte[] sendValue, String uuid1) {
        UUID uuid = BeanBleUtil.parseUUID (uuid1);
        boolean su = false;
        LogS.d (TAG, "writeByte() sendValue: " + sendValue.toString () + ", uuid: " + uuid);
        if (currentBindGatt != null) {
            BluetoothGattService bleService = currentBindGatt.getService (BeanBleUtil.parseUUID (”181c"));
            BluetoothGattCharacteristic characteristic = bleService.getCharacteristic (uuid);
            if (characteristic != null) {
                characteristic.setValue (sendValue);
                su = currentBindGatt.writeCharacteristic (characteristic);
    
            }
        }
        return su;
    }

如果写入成功onCharacteristicWrite 回掉函数会执行。

9.关于UUID

UUID 本身指的是一个类UUID ,他包含uuid ,我们所说的特征值id。
uuid 是一串十六进制组成的数字,提示唯一识别码,就像身份证一样,只不过ble中它代表服务,特征的号码,uuid 是128bit 的,因为太长所有SIG将uuid 进行预设,预设过后uuid 的一些部分就变成了固定部分,就比如我们本次服务的uuid 是: “0000” + 181c+ “-0000-1000-8000-00805f9b34fb”;
除了181c ,其他部分都是固定的。

最后:对于很对没有开发过蓝牙的同学来说需要搞明白,ble 是不需要去配对的,需要配对的是传统蓝牙,这点不要傻傻分不清楚,其次,ble 蓝牙只需要搜索到设备,可以直接连接了,过程相对简单,但是任何设备在连接的过程中都可能发生异常,所以我建议在连接的时候设置超时机制,可以有效减少程序故障。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值