Android BLE 蓝牙开发指南(三)外围设备端开发详解

Android BLE开发指南(一)入门基础
Android BLE开发指南(二)中心设备端程序开发详解

这篇文章将会详细讲解低功耗蓝牙外围设备端程序开发的主要流程。对于Android开发者而言,或许涉及外围设备端程序的开发需求不多,但是本着深入学习的原则,最好也对外围设备端程序的运行流程有个了解。这样在开发低功耗蓝牙相关功能遇到问题时,或许能帮你更快的定位到问题原因。

1. 配置权限

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

2. 检查蓝牙开关

同样的,在进行BLE的相关操作前,确保蓝牙开关已经打开。

    private BluetoothAdapter mBtAdapter;
    public void init(Application application) {
        ......
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    public boolean isBleEnabled() {
        return mBtAdapter != null && mBtAdapter.isEnabled();
    }

如果开关未开启,则向用户申请开启蓝牙开关

    public boolean setBleEnable(Activity activity, boolean enable) {
        boolean setSucceed = true;
        if (enable) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            activity.startActivityForResult(intent, BleManager.REQUEST_ENABLE_BT);
        } else {
            ......
        }
        return setSucceed;
    }

用户同意或者拒绝蓝牙开关申请后,会通过 onActivityResult 将结果进行反馈,如果蓝牙开启成功,则继续下一步操作。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK && requestCode == BleManager.REQUEST_ENABLE_BT) {
            startPeripheralServer();
        }
    }

3. 开启低功耗蓝牙广播

要想让设备能被其它BLE设备扫描到,就要先开启低功耗蓝牙广播。
之前说过,Android 5.0 (API Level 21)开始才提供了对外围设备相关的API支持。所以当前系统版本如果低于 API Level 21,那么不支持外围设备模式,直接中止操作并退出。

首先,通过 BluetoothAdapter 拿到 BluetoothLeAdvertiser 的实例对象,然后进行广播前的配置初始化工作,可以看到,开启广播的 startAdvertising 函数传入了三个参数,我们一个个来看。
AdvertiseSettings: 提供了广播模式等一些自定义选项的配置,一般情况下使用以下默认配置即可;
AdvertiseData:广播时的附带的数据信息。这里可以通过 addServiceUuid 加入一个UUID信息。这样中心设备在扫描到该外围设备后,可以获取到这个UUID,从而帮助中心设备判断是否找到了目标设备;
AdvertiseCallback:回调方法,在开启广播操作完成后,会有回调结果。

    private BluetoothAdapter mBtAdapter;
    private BluetoothLeAdvertiser mBleAdvertiser;
    private BluetoothGattServer mBtGattServer;
    private AdvertiseCallback mAdsCallback;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public boolean startPeripheralServer(... ...) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            LogUtil.w("current SDK version is not supported !!!");
            return false;
        }

        ......
        if (mBleAdvertiser == null) {
            mBleAdvertiser = mBtAdapter.getBluetoothLeAdvertiser();
        }
        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setConnectable(true)
                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER)
                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
                .setTimeout(0)
                .build();

        AdvertiseData advertiseData = new AdvertiseData.Builder()
                .addServiceUuid(new ParcelUuid(UUIDUtil.UUID_DEVICE))
                .setIncludeTxPowerLevel(true)
                .setIncludeDeviceName(true)
                .build();

        mAdsCallback = new AdvertiseCallback() {
            @Override
            public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                LogUtil.i("!!! startAdvertising is succeed ");
                initBleService();
                ......
            }

            @Override
            public void onStartFailure(int errorCode) {
                LogUtil.w("!!! startAdvertising is failed. with code: " + errorCode);
                ......
            }
        };

        mBleAdvertiser.startAdvertising(settings, advertiseData, mAdsCallback);
        return true;
    }

调用 startAdvertising 开启BLE广播后,如果开启成功,则会回调 AdvertiseCallback 接口中的 onStartSuccess 函数,那么接下来就可以开始初始化BLE服务了。

4. 初始化低功耗蓝牙服务

二话不说,反手就 new 一个BLE服务——BluetoothGattService,需要传入两个参数。每个服务对应一个UUID,这里我们事先定义好了一个UUID,然后将服务类型定义为 SERVICE_TYPE_PRIMARY,表示该服务为主服务。

Android BLE开发指南(一)入门基础 说过,一个服务中包含一个或以上的 Characteristic,用于数据的传输。这里定义了两个 Characteristic:
writableCharacteristic 用于中心设备向外围设备发送数据;
mNotifyCharacteristic 用于外围设备向中心设备发送数据。

    private BluetoothGattServer mBtGattServer;
    private BluetoothGattServerCallback mBtGattServerCallback;

    private void initBleService() {
        if (mBtGattServer == null) {
            BluetoothManager btManager = (BluetoothManager)
                    mContext.getSystemService(Context.BLUETOOTH_SERVICE);
            mBtGattServer = btManager.openGattServer(mContext, mBtGattServerCallback);
        }
        if (mBtGattServer == null) {
            LogUtil.w("!!! openGattServer is failed");
            return;
        }

        BluetoothGattService gattService = new BluetoothGattService(
                UUIDUtil.UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);
        BluetoothGattCharacteristic writableCharacteristic = new BluetoothGattCharacteristic(
                UUIDUtil.UUID_CHARIC_WRITABLE,
                BluetoothGattCharacteristic.PROPERTY_WRITE
                        | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
                BluetoothGattCharacteristic.PERMISSION_WRITE);
        mNotifyCharacteristic = new BluetoothGattCharacteristic(
                UUIDUtil.UUID_CHARIC_READABLE,
                BluetoothGattCharacteristic.PROPERTY_READ
                        | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
                BluetoothGattCharacteristic.PERMISSION_READ);
        gattService.addCharacteristic(writableCharacteristic);
        gattService.addCharacteristic(mNotifyCharacteristic);
        mBtGattServer.clearServices();
        mBtGattServer.addService(gattService);
    }

走到这里,运行该程序的外围设备 就可以被其它中心设备扫描到了。

5. 监控设备连接情况

那么,怎么知道有没有其它设备跟我建立连接了呢?
回头看一下,我们发现在初始化BLE服务的时候,openGattServer 函数中传入一个回调接口 BluetoothGattServerCallback,轮到它出场了。

在与其它BLE设备建立连接或者断开连接的时候,会回调 onConnectionStateChange 函数,
其中, newState 等于 BluetoothProfile.STATE_CONNECTED 表示有设备建立连接;而 newState 等于 BluetoothProfile.STATE_DISCONNECTED 表示有设备断开连接。

根据这两个状态,设备连接情况就尽在掌握啦~

    private BluetoothGattServerCallback mBtGattServerCallback = new BluetoothGattServerCallback() {
        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    mConnDevController.addConnectedDevice(device.getAddress(), device);
                    ......
                    break;

                case BluetoothProfile.STATE_DISCONNECTED:
                    mConnDevController.removeConnectedDevice(device.getAddress());
                    ......
                    break;
            }
        }

        ......
    };

6. 接收数据

还是熟悉的老面孔——BluetoothGattServerCallback。当接收到数据时,会回调接口中的 onCharacteristicWriteRequest 函数,接收到的数据就是一个byte数组value,数据量最大就20个byte;另外参数 device 包含了一些设备信息,可以用来识别是哪个设备发送过来的数据。

这里需要说明一下的是,在测试中发现,回调 onCharacteristicWriteRequest 函数时,需要调用下 mBtGattServer.sendResponse ,否则设备连接会莫名其妙断开。具体原因有待研究。

    private BluetoothGattServerCallback mBtGattServerCallback = new BluetoothGattServerCallback() {
        ......

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            LogUtil.w("!!! onCharacteristicWriteRequest: " + new String(value));
            mBtGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
            ......
        }
    };

7. 数据发送

之前定义的 mNotifyCharacteristic 就是用于发送数据的。
首先需要明确需要发送数据到哪个目标设备,然后通过目标设备的MAC地址,获取到之前缓存的目标设备的 BluetoothDevice 实例。接着往 mNotifyCharacteristic 存入需要发送的数据,最后调用 notifyCharacteristicChanged 完成数据的发送。

    public boolean sendDataToCentralDevice(String deviceAddress, String data) {
        if (mNotifyCharacteristic == null) {
            return false;
        }

        boolean succeed = false;
        BluetoothDevice btDevice = mConnDevController.getConnectedDevice(deviceAddress);
        if (btDevice != null) {
            mNotifyCharacteristic.setValue(data);
            succeed = mBtGattServer.notifyCharacteristicChanged(btDevice,
                    mNotifyCharacteristic, false);
        }
        return succeed;
    }

数据发送完成后,中心设备端程序 BluetoothGattCallback 中的 onCharacteristicChanged 函数会被回调(在 Android BLE开发指南(二)中心设备端程序开发详解 讲过就不赘述了)。

8. 关闭低功耗蓝牙广播

如果想隐藏自己不被找到,那就调用 stopAdvertising 吧,最后把 BluetoothGattServer close掉。

    public boolean stopPeripheralServer() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return false;
        }
        mBtGattServerListener = null;
        mNotifyCharacteristic = null;
        if (mBleAdvertiser != null) {
            mBleAdvertiser.stopAdvertising(mAdsCallback);
        }
        if (mBtGattServer != null) {
            mBtGattServer.close();
        }
        return true;
    }

9. Demo运行示例图

程序运行示例图如下,左边是中心设备端程序的界面,右边是外围设备端程序的界面。
中心设备发送 “central dev data” 到外围设备,外围设备发送 ”peripheral dev data“ 到中心设备,两个设备都收到了对方发送的数据并显示在了界面上。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值