Android BLE开发实战和总结

最近项目中需要使用到BLE通信,用来实现车机端和另外一个设备的信息交互(称为从设备)。在做了一番了解之后发现,Android对于BLE的封装已经很到位了,使得BLE的功能相对于整个Android蓝牙SDK来说就是一个Profile,使用起来已经很方便。

一、BLE基本概念:

1.概述

BLE全称为Bluetooth Low Energy,低功耗蓝牙技术,用以解决传统蓝牙(BR)功耗过高的问题。其中定义了两个角色,一个是主设备(Master),如手机、车机等处理能力较强的设备;一个是从设备(Slave),通常是智能手环、心跳仪等外围设备。一个主设备可以同时连接多个从设备,而一个从设备也可以同时连接多个主设备。


2.广播

在主设备跟从设备建立连接之前,从设备需要每隔一段时间就发送一条广播信号,实际上是在37(2402MHz) /38(2426MHz) /39(2480MHz)这三个信道上发送相同的广播,广播间隔(Advertising interval )在20ms ~ 10.28s之间。一条广播信息中包含设备地址(device address)、设备名、数据类型、自定义数据字段等,长度不能超过31个字节。
当主设备打开扫描功能之后,会在三个信道上分时开启接收窗口,一旦在某个信道上的接收到广播数据,那么就可以扫描到从设备了。


3.连接

由于广播携带的数据有限,如果两个设备间需要交互数据,那么就需要建立连接。所谓建立连接,其实就是两个设备之间达成收发数据的物理信道一致、时间原点一致、基于时间原点的收发时间间隔一致,从而实现一对一的通信。
当主设备端收到从设备的广播之后,就可以对从设备发起连接请求。从设备在每次广播之后,会开启一段时间的信息接收窗口,这个时候从设备收到连接请求,就会给主设备反馈连接成功的信号。后面两个设备就会定时发送数据和接收数据,完成数据的交互。
当然,以上是链路层的逻辑,而上层协议栈中,实际上是建立了GATT协议的连接。


4.GATT协议

GATT协议中定义了两个角色,一个是Service,一个是Characteristic,每个Service可以包含多个Characteristic,且他们都有特定的UUID,类似于 0000ff00-0000-1000-8000-00805f9b34fb 这样的字符串。每个Service代表提供某种服务的能力,比如跟心率有关的Service;Characteristic代表的是一个键值对,Service就是通过这个一个个的键值对达到传输数据的目的。他们的关系如下图所示:
在这里插入图片描述

当主设备和从设备连接上GATT协议之后,就可以询问从设备可以提供哪些服务,在得到从设备的反馈报文后,就可以通过双方协商好的UUID获取到Service服务,然后再从Service中根据UUID获取到可读的Characteristic和可写的Characteristic,操作Characteristic就可以实现具体的数据通信。

二、Android中的BLE

1.扫描设备

连接BLE设备,首先需要扫描。Android SDK中使用BluetoothLeScanner对象来执行扫描的动作:

private BluetoothLeScanner bluetoothLeScanner;

bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

bluetoothLeScanner.startScan(scanCallback);

由于扫描是一个异步的过程,所以这里需要传入一个回调接口,我们在回调接口去获取扫描的结果:

public ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            ScanRecord record = result.getScanRecord();
            BluetoothDevice device = result.getDevice();
            String deviceName = record.getDeviceName();
            Log.d(TAG, "record name:" + deviceName);
            Log.d(TAG, "ServiceUuids:" + record.getServiceUuids());

            if (TextUtils.isEmpty(deviceName)) {
                return;
            }

            if (deviceName.startsWith("XX")) {    //这里我们可以找出设备名以XX开头的BLE设备
              
                byte[] bytes = record.getBytes();    //这里可以获取整个广播的完整数据,包括协议头等
                for (int i = 0; i < bytes.length; i++) {
                    Log.d(TAG, "[" + i + "]:" + bytes[i]);
                }
                
                bluetoothLeScanner.stopScan(scanCallback);     //需要停止扫描
                              
            }
        }

    };

2.连接设备

在找到了“XX”名称的设备后,我们就可以发起GATT协议的连接了:

device.connectGatt(context, false, gattCallback);

第二个参数,如果是false代表仅发起本次连接,如果连接不上则会反馈连接失败;如果是true则表示只要这个远程的设备可用,那么底层协议栈就会自动去连接,并且第一次连接不上,也会继续去连接。
第三个参数,是一个关于GATT协议相关的回调接口,主要有GATT连接状态的回调、发现Service服务的回调、特征值(Characteristic)发生改变的回调、最大传输单元(MTU)改变的回调、物理层发送模式(PHY)改变回调等,如下:

BluetoothGattCallback gattCallback = new BluetoothGattCallback() {

        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
            Log.d(TAG, "onPhyUpdate txPhy:" + txPhy + "; rxPhy:" + rxPhy);
        }

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

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            Log.d(TAG, "onConnectionStateChange newState:" + newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {  //协议连接成功
                Log.d(TAG, "STATE_CONNECTED");
                bluetoothGatt = gatt;             
                bluetoothGatt.discoverServices();   //发现service服务
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {         //协议连接失败
                Log.d(TAG, "STATE_DISCONNECTED");
            }

        }
}

3.获取服务和特征(Characteristic)

在GATT协议连接成功之后,就可以去发现从设备端提供了哪些Service服务,如上代码。
这是一个异步的过程,待从设备反馈了自己提供的服务之后,Android框架层会通过BluetoothGattCallback回调通知,如下:

BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
	  @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            List<BluetoothGattService> services = gatt.getServices();
            for (BluetoothGattService service : services) {
                Log.d(TAG, "UUID:" + service.getUuid().toString());
            }
			
			//1.根据UUID获取到服务
            mGattService = gatt.getService(UUID.fromString("0000ff00-0000-1000-8000-00805f9b34fb"));
           
            if (mGattService == null) {
                Log.w(TAG, "GattService is null!");
            } else {
                Log.i(TAG, "connect GattService");
                if(writeCharacteristic == null){
                //2.获取一个特征(Characteristic),这是从设备定义好的,我通过这个Characteristic去写从设备感兴趣的值
                    writeCharacteristic = mGattService
                            .getCharacteristic(UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb"));
                }
                if(readCharacteristic == null){
                //3.获取一个主设备需要去读的特征(Characteristic),获取从设备发送过来的数据
                    readCharacteristic = mGattService
                            .getCharacteristic(UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb"));

                 //4.注册特征(Characteristic)值改变的监听
                    bluetoothGatt.setCharacteristicNotification(readCharacteristic, true);
                    List<BluetoothGattDescriptor> descriptors = readCharacteristic.getDescriptors();
                    for (BluetoothGattDescriptor descriptor : descriptors) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                        bluetoothGatt.writeDescriptor(descriptor);
                    }
                }             

        }
}

经过上述代码中的四个步骤,两个设备间已经可以发送和接收数据了。

4.通过特征(Characteristic)发送数据

把需要发送的数据设置到writeCharacteristic,然后再调用BluetoothGatt的写入方法,即可完成数据的发送:

writeCharacteristic.setValue(datas);

bluetoothGatt.writeCharacteristic(writeCharacteristic);

5.读取数据

当从设备有数据发送到主设备之后,Android系统会回调BluetoothGattCallback的onCharacteristicChanged方法通知:

@Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            UUID uuid = characteristic.getUuid();
            byte[] receiveData = characteristic.getValue();
            for (byte b : receiveData) {
                Log.d(TAG, "receiveData:" + Integer.toHexString(b));
            }
        }

三、注意事项

1.自动连接属性

connectGatt方法的自动连接参数设置为true之后,连接建立了,这个时候如果是断开连接,如下:

bluetoothGatt.disconnect();

虽然在Android层面的BluetoothGattCallback接口会立刻反馈一个STATE_DISCONNECTED信号值,但是在数据链路层却还是处于连接的状态,连接并没有断开。

2.开启定位功能

现在Android最新的版本,需要开启定位才能使用BLE功能。
判断定位功能是否开启:

private boolean isLocationEnable(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        boolean networkProvider = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        boolean gpsProvider = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (networkProvider || gpsProvider) {
            return true;
        }
        return false;
    }

开启定位功能的方法:

 LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            try {
                Field field = UserHandle.class.getDeclaredField("SYSTEM");
                field.setAccessible(true);
                UserHandle userHandle = (UserHandle) field.get(UserHandle.class);
                Method method = LocationManager.class.getDeclaredMethod(
                        "setLocationEnabledForUser",
                        boolean.class,
                        UserHandle.class);
                method.invoke(locationManager, true, userHandle);
            } catch (Exception e) {
            }

3.最大传输单元(MTU)的设置

Android默认的最大传输单元(MTU)是23个字节,除去报文头占用的3个字节,实际最大只能传递20个字节。当两个设备之间传递的数据长度超过20字节的时候,数据就会被截断,导致通信异常。
只有在GATT协议连接成功之后,才可以设置MTU值,最大MTU=512,如下:

bluetoothGatt.requestMtu(128);

4.从设备广播间隔影响连接

当Android协议栈(Host)给蓝牙芯片Chip发送一个连接的指令,芯片在收到之后,会在一定的时间内去接收从设备的广播,在收到广播之后才会发送连接请求给从设备;如果从设备的广播间隔设置不合理,就会导致芯片无法在限定的时间内收到广播,导致无法发送连接请求。BLE连接过程如下图:
在这里插入图片描述

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android BLE开发是指在Android平台上使用蓝牙低功耗(BLE)技术进行应用程序开发BLE是一种省电的蓝牙通信技术,被广泛应用于智能穿戴设备、医疗设备、家居设备等领域。 Android平台提供了一套完整的API来支持BLE开发开发者可以使用这些API来搜索和连接BLE设备、发送和接收数据、读取和写入BLE特征值等等。 首先,开发者需要在AndroidManifest.xml文件中添加必要的权限,如蓝牙和蓝牙管理器权限。然后,在代码中实例化一个BluetoothManager对象来获取BluetoothAdapter(蓝牙适配器)实例。 接下来,开发者可以使用BluetoothAdapter的方法来搜索和连接BLE设备。搜索时,可以通过实现BluetoothAdapter.LeScanCallback接口来获取搜索到的设备信息。连接时,可以通过实现BluetoothGattCallback接口来处理与设备的通信。 一旦成功连接到BLE设备,开发者可以使用BluetoothGatt对象来发送和接收数据。通过BluetoothGatt对象,可以发现服务和特征值,读取和写入特征值等等操作。同时,开发者也可以监控设备发出的通知和指示。 在开发过程中,开发者还需要注意BLE通信的一些特点。例如,BLE是基于事件驱动的,所以开发者需要处理相关的回调方法;BLE设备的连接是一种异步过程,所以开发者需要在连接过程中处理各种状态;BLE通信是基于GATT协议,开发者需要熟悉相关的概念和操作等。 总而言之,Android BLE开发提供了一种在Android平台上与BLE设备进行通信的方式。通过使用AndroidBLE API,开发者可以方便地实现与BLE设备的连接和数据传输,为开发各种BLE应用程序提供了便利。 ### 回答2: Android BLE开发是指在Android设备上使用BLE(蓝牙低功耗)技术进行应用开发的过程。BLE是一种蓝牙技术,相比传统的蓝牙技术具有低功耗、简单、成本低等优势,适用于低功耗设备之间的通信。 在Android BLE开发中,首先需要通过在AndroidManifest.xml文件中声明蓝牙权限来获取蓝牙访问权限。然后,需要使用BluetoothAdapter类来获取蓝牙适配器,并检查设备是否支持BLE功能。 接下来,在开发中需要使用BluetoothGatt类来建立与远程BLE设备的连接和通信。使用BluetoothGattCallback类可以监听到连接状态的改变,以及接收到的数据。 在与BLE设备通信时,需要使用GATT(通用属性配置配置文件)协议来发送和接收数据。GATT协议通过将数据分为服务(Service)和特征(Characteristic)进行管理。服务代表一个特定的功能,而特征代表服务的具体属性。 在开发过程中,还可以使用BluetoothLeScanner类进行扫描周围的BLE设备。当发现设备后,可以通过BluetoothDevice类来获取设备的详细信息,如设备名称、MAC地址等。 总结来说,Android BLE开发需要了解蓝牙低功耗技术以及相关API的使用。通过建立连接、发送数据、接收数据等操作,可以实现与BLE设备的通信。开发人员需要注意处理连接状态、数据解析等问题,以确保应用的可靠性和稳定性。 ### 回答3: Android BLE开发是指基于Android系统的蓝牙低功耗(Bluetooth Low Energy,以下简称BLE)技术进行应用开发的过程。 在Android BLE开发中,首先需要进行设备扫描。通过使用与蓝牙相关的API,我们可以搜索附近的BLE设备并获取设备的相关信息,例如设备名称、信号强度、MAC地址等。扫描到设备后,可以使用设备的唯一标识符(UUID)进行连接。 连接设备后,可以进行数据通信。BLE通信主要通过GATT(通用属性配置文件)协议进行,该协议规定了BLE设备和Android应用之间的数据传输格式和规则。开发者可以通过GATT API访问BLE设备的服务和特征,读取和写入相应的属性值。 在数据通信过程中,也可以进行数据处理。开发者可以对从BLE设备接收到的数据进行解析、处理和展示。例如,对传感器采集的数据进行分析、计算和展示,或者根据接收到的数据进行特定的操作和控制。 在开发过程中,还需要注意一些注意事项。例如,保持正确的扫描周期,避免频繁的连接和断开操作,合理处理设备不可用的情况等等。此外,对于BLE通信的兼容性,开发者应考虑不同设备的支持情况,以确保应用在各种Android设备上的正常运行。 总结来说,Android BLE开发是在Android平台上利用BLE技术进行应用开发的过程。通过设备扫描、连接和数据通信,开发者可以实现与BLE设备之间的无线数据交互。通过合理的数据处理和注意事项的考虑,可以提高应用的稳定性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值