欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android开发总结之低功耗蓝牙开发

程序员文章站 2024-03-25 00:01:41
...

前言

因笔者工作需要,开发一款蓝牙秤的数据读取软件。有些心得,便在此记录。俗话说:好记性不如烂笔头。笔者也是为了备忘。写下本篇博客。帮助有需要的同学~

本篇文章来源于开发者指导翻译,开发者指导示例代码。以及自己的一些心得。帮助不会蓝牙低功耗的同学快速入门。

本篇博客有大多数都是官网直译,小部分为自己心得。如有翻译出错地方,及文章错误。望众大神在评论区指出。笔者定勤加修改

低功耗蓝牙概述

Android 4.3(API级别18)引入了内置平台支持蓝牙低功耗(BLE)的核心角色,并提供应用程序可用于发现设备,查询服务和传输信息的API。

常见用例包括以下内容:

  • 在附近设备之间传输少量数据。
  • 与Google Beacons等接近传感器进行交互,根据用户的当前位置为用户提供定制体验。

与传统蓝牙相比,蓝牙低功耗(BLE)旨在提供显着降低的功耗。 这允许Android应用程序与具有更严格电源要求的BLE设备通信,例如接近传感器,心率监视器和健身设备。

关键术语和概念

以下是关键BLE术语和概念的摘要:

  • Generic Attribute Profile (GATT) ——GATT配置文件是用于在BLE链路上发送和接收称为“属性”的短数据的通用规范。 目前所有低能耗应用配置文件均基于GATT。

  • Bluetooth SIG为低能耗设备定义了许多配置文件。 配置文件是设备在特定应用程序中的工作方式的规范。 请注意,设备可以实现多个配置文件。 例如,设备可以包含心率监测器和电池水平检测器。

  • Attribute Protocol (ATT) ——GATT建立在属性协议(ATT)之上。 这也称为GATT / ATT。 ATT经过优化,可在BLE设备上运行。 为此,它使用尽可能少的字节。 每个属性由通用唯一标识符(UUID)唯一标识,UUID是用于唯一标识信息的字符串ID的标准化128位格式。 ATT传输的属性被格式化为特征和服务。

  • Characteristic——特征包含单个值和描述特征值的0-n描述符。 特征可以被认为是类型,类似于类。

  • Descriptor——描述符是定义描述特征值的属性。 例如,描述符可以指定人类可读的描述,特征值的可接受范围,或特征值特定的度量单位。

  • Service——服务是一系列特征。 例如,您可以使用名为“心率监测器”的服务,其中包括“心率测量”等特征。 您可以在bluetooth.org上找到基于GATT的现有配置文件和服务的列表。

角色和责任

以下是Android设备与BLE设备交互时应用的角色和职责:

  • *与外围(Central vs. peripheral)。 这适用于BLE连接本身。 中心角色的设备扫描,寻找广告,外围角色的设备制作广告。
  • GATT服务器与GATT客户端。 这确定了两个设备建立连接后如何相互通信。

低功耗蓝牙权限

要在您的应用程序中使用蓝牙功能,您必须声明蓝牙权限BLUETOOTH。 您需要此权限才能执行任何蓝牙通信,例如请求连接,接受连接和传输数据。

如果您希望应用程序启动设备发现或操作蓝牙设置,则还必须声明BLUETOOTH_ADMIN权限。 注意:如果使用BLUETOOTH_ADMIN权限,则还必须具有BLUETOOTH权限。

在应用程序清单文件中声明蓝牙权限。 例如:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--如果需要搜索蓝牙设备,需要加上定位权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

如果您要声明您的应用仅适用于支持BLE的设备,请在应用清单中包含以下内容:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

但是,如果您希望将应用程序提供给不支持BLE的设备,您仍应将此元素包含在应用程序的清单中,但必须设置required =“false”。 然后在运行时,您可以使用PackageManager.hasSystemFeature()确定BLE可用性:

// 使用此检查确定设备是否支持BLE。 然后,您可以有选择地禁用与BLE相关的功能。
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

注意:LE Beacons通常与位置相关联。 要使用BluetoothLeScanner,您必须通过在应用程序的清单文件中声明ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION权限来请求用户的权限。 没有这些权限,扫描将不会返回任何结果。

设置低功耗蓝牙

在您的应用程序可以通过BLE进行通信之前,您需要验证设备是否支持BLE,如果是,请确保它已启用。 请注意,仅当<uses-feature … />设置为false时才需要进行此检查。

如果不支持BLE,则应优雅地禁用任何BLE功能。 如果BLE受支持但已禁用,则您可以请求用户启用蓝牙而无需离开您的应用程序。 使用BluetoothAdapter,可以分两步完成此设置。

  • 获取BluetoothAdapter
    任何和所有蓝牙活动都需要BluetoothAdapter。 BluetoothAdapter代表设备自己的蓝牙适配器(蓝牙无线电)。 整个系统都有一个蓝牙适配器,您的应用程序可以使用此对象与其进行交互。 下面的代码段显示了如何获取适配器。 请注意,此方法使用getSystemService()返回BluetoothManager的实例,然后使用该实例获取适配器。 Android 4.3(API Level 18)介绍了BluetoothManager:
private BluetoothAdapter bluetoothAdapter;

final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
  • 启用蓝牙
    接下来,您需要确保启用蓝牙。 调用isEnabled()检查当前是否启用了蓝牙。 如果此方法返回false,则禁用蓝牙。 以下代码段会检查是否已启用蓝牙。 如果不是,则代码段会显示错误提示用户转到“设置”以启用蓝牙:
// 确保设备上可以使用蓝牙并启用蓝牙。 如果没有,则显示一个对话框,请求用户启用蓝牙权限。
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

注意:传递给startActivityForResult(android.content.Intent,int)的REQUEST_ENABLE_BT常量是一个本地定义的整数(必须大于0),系统会在你的onActivityResult(int,int,android.content)中传回给你。 Intent)实现为requestCode参数。

找到低功耗设备

要查找BLE设备,请使用BluetoothLeScanner.startScan(ScanCallback)方法。 此方法将ScanCallback作为参数。 您必须实现此回调,因为这是返回扫描结果的方式。 由于扫描是非常浪费资源的,因此您应遵守以下准则:

  • 找到所需设备后,请立即停止扫描。
  • 切勿循环扫描,并设置扫描时间限制。 之前可用的设备可能已超出范围,继续扫描会消耗资源。

以下代码段显示了如何启动和停止扫描:

/**
 * 停止蓝牙扫描
 */
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void stopScanLeDevice(ScanCallback scanCallback) {
    if (isEnable && mBluetoothAdapter.isEnabled()) {
        BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        bluetoothLeScanner.stopScan(scanCallback);
    }
}

/**
 * 开始蓝牙扫描
 */
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void scanLeDevice(ScanCallback scanCallback) {
    if (isEnable && mBluetoothAdapter.isEnabled()) {
        BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        bluetoothLeScanner.startScan(scanCallback);
    }
}


以下是ScanCallback的实现,它是用于提供BLE扫描结果的接口:

    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            result.getDevice();//蓝牙设备
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            L.e("批次扫描结果-onBatchScanResults");
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            L.e("扫描失败:onScanFailed-errorCode:" + errorCode);
        }
    };

连接到GATT 服务

与BLE设备交互的第一步是连接到它 - 更具体地说,连接到设备上的GATT服务器。 要连接到BLE设备上的GATT服务器,请使用connectGatt()方法。 此方法有三个参数:一个Context对象,autoConnect(指示是否在可用时自动连接到BLE设备),以及对BluetoothGattCallback的引用:

mBluetoothGatt = device.connectGatt(mContext, true, mBluetoothGattCallback);

这将连接到BLE设备托管的GATT服务器,并返回一个BluetoothGatt实例,然后您可以使用该实例执行GATT客户端操作。 呼叫者(Android应用)是GATT客户端。 BluetoothGattCallback用于向客户端提供结果,例如连接状态,以及任何进一步的GATT客户端操作。

Gatt服务器状态改变会调用监听器中的回调函数:

private final BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        L.e("===========\t连接状态改变\t===========");
        if (newState == BluetoothProfile.STATE_CONNECTING) {
            L.e("-----------\t连接中....\t-----------");
        } else if (newState == BluetoothProfile.STATE_CONNECTED) {
            L.e("连接到GATT服务器\n启动搜索GATT服务器存在的服务:" +
                    (mBluetoothGatt.discoverServices() ? "启动成功" : "启动失败"));
                    //调用discoverServices函数会触发onServicesDiscovered函数
            //停止扫描
            mBluetoothController.stopScanLeDevice(mScanCallback);
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED || newState == BluetoothProfile.STATE_DISCONNECTING) {
            L.e("与GATT服务器断开连接.");
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        //发现了新服务
        if (status == BluetoothGatt.GATT_SUCCESS) {
            displayGattServices(gatt);
        } else {
            L.e("onServicesDiscovered received: " + status);
        }
    }
    @Override
    // 特征读取操作的结果
    public void onCharacteristicRead(BluetoothGatt gatt,  BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
        }
    }

    /**
     * 特征值改变
     */
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);        
        byte[] data = characteristic.getValue();
    }
};

读取 BLE 属性

一旦您的Android应用程序连接到GATT服务器并发现了服务,它就可以在支持的位置读取和写入属性。

private void displayGattServices(BluetoothGatt gatt) {
    List<BluetoothGattService> gattServices = gatt.getServices();
    if (gattServices == null) return;

    for (BluetoothGattService gattService : gattServices) {

        for (BluetoothGattCharacteristic bluetoothGattCharacteristic : gattService.getCharacteristics()) {
        
        }
    }
}

收到GATT通知

BLE应用程序通常会要求在设备上的特定特征发生变化时收到通知。此代码段显示如何使用setCharacteristicNotification()方法设置特征的通知:

mBluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(
        bluetoothGattCharacteristic.getUuid());
if (descriptor != null) {
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

    mBluetoothGatt.writeDescriptor(descriptor);
}

为特性启用通知后,如果远程设备上的特性发生更改,则会触发onCharacteristicChanged()回调:

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    super.onCharacteristicChanged(gatt, characteristic);
    byte[] data = characteristic.getValue();
}

释放资源

当应用程序使用完蓝牙设备应该将其关闭,以节约资源、电量。

@Override
protected void onDestroy() {
    if (mBluetoothGatt != null) {
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }
    super.onDestroy();
}

本篇博客的完整代码已经上传至GitHub:BlueToothLeClientDemo

相关标签: 蓝牙低功耗