【Android -- 蓝牙】BLE 蓝牙开发入门

初识低功耗蓝牙

Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序通过这些 API 扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等操作。

Android BLE 使用的蓝牙协议是 GATT 协议,有关该协议的详细内容可以参见蓝牙官方文档。以下我引用一张官网的图来大概说明 Android 开发中我们需要了解的一些 Bluetooth Low Energy 的专业术语。
在这里插入图片描述

1. Service

一个低功耗蓝牙设备可以定义许多 Service, Service 可以理解为一个功能的集合。设备中每一个不同的 Service 都有一个 128 bit 的 UUID 作为这个 Service 的独立标志。蓝牙核心规范制定了两种不同的UUID,一种是基本的UUID,一种是代替基本UUID的16位UUID。所有的蓝牙技术联盟定义UUID共用了一个基本的UUID:
0x0000xxxx-0000-1000-8000-00805F9B34FB
为了进一步简化基本UUID,每一个蓝牙技术联盟定义的属性有一个唯一的16位UUID,以代替上面的基本UUID的‘x’部分。例如,心率测量特性使用0X2A37作为它的16位UUID,因此它完整的128位UUID为:
0x00002A37-0000-1000-8000-00805F9B34FB

2. Characteristic

在 Service 下面,又包括了许多的独立数据项,我们把这些独立的数据项称作 Characteristic。同样的,每一个 Characteristic 也有一个唯一的 UUID 作为标识符。在 Android 开发中,建立蓝牙连接后,我们说的通过蓝牙发送数据给外围设备就是往这些 Characteristic 中的 Value 字段写入数据;外围设备发送数据给手机就是监听这些 Charateristic 中的 Value 字段有没有变化,如果发生了变化,手机的 BLE API 就会收到一个监听的回调。

更详细的内容可以参见:

二、Android BLE API 简介

  • BluetoothAdapter
    BluetoothAdapter 拥有基本的蓝牙操作,例如开启蓝牙扫描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)实例化一个 BluetoothDevice 用于连接蓝牙设备的操作等等。

  • BluetoothDevice
    代表一个远程蓝牙设备。这个类可以让你连接所代表的蓝牙设备或者获取一些有关它的信息,例如它的名字,地址和绑定状态等等。

  • BluetoothGatt
    这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。

  • BluetoothGattService
    这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。

  • BluetoothGattCharacteristic
    这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。

三、梳理整体逻辑(思路/步骤)

  1. 权限问题:先判断手机是否满足android4.3以上版本,再判断手机是否开启蓝牙。
  2. 搜索蓝牙:搜索蓝牙,回调接口中查看ble设备相关信息,一定时间停止扫描。
  3. 连接蓝牙:首先获取到ble设备的mac地址,然后调用connect()方法进行连接。
  4. 获取特征:蓝牙连接成功后,需要获取蓝牙的服务特征等,然后开启接收设置。
  5. 发送消息:writeCharacteristic()方法,发送数据给ble设备。
  6. 接收消息:通过蓝牙的回调接口中onCharacteristicRead()方法,接收蓝牙收的消息。
  7. 释放资源:断开连接,关闭资源。

四、实战

在这里插入图片描述在这里插入图片描述

1. 权限问题

  • 在 AndroidManifest.xml 中声明权限
	<!-- 蓝牙所需权限 -->
    <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" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-feature android:name="android.hardware.location.gps" />
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />
  • 获取蓝牙适配器
  BluetoothManager  mBluetoothManager =(BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
  BluetoothAdapter  mBluetoothAdapter = mBluetoothManager.getAdapter();
  • 判断手机蓝牙是否被打开
mBluetoothAdapter.isEnabled()

如果返回true,这个时候就可以扫描了
如果返回false,这时候需要打开手机蓝牙。 可以调用系统方法让用户打开蓝牙。

 Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
 startActivity(enable);

2. 搜索蓝牙

  • 开始扫描
//10s后停止搜索
new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, 1000 * 10);
 
UUID[] serviceUuids = {UUID.fromString(service_uuid)};
mBluetoothAdapter.startLeScan(serviceUuids, mLeScanCallback);
  • 扫描的回调
//蓝牙扫描回调接口
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback(){
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            if (device.getName() == null) {
                return;
            }
            Log.e("--->搜索到的蓝牙名字:", device.getName());
            //可以将扫描的设备弄成列表,点击设备连接,也可以根据每个设备不同标识,自动连接。
 
        }
    };

3. 连接蓝牙

  • 获取设备的mac地址,然后连接。
  //获取所需地址
  String mDeviceAddress = device.getAddress();
  BluetoothGatt mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
  • onConnectionStateChange()被调用
    连接状态改变时,mGattCallback中onConnectionStateChange()方法会被调用,当连接成功时,需要调用
    mBluetoothGatt.discoverServices();去获取服务。

  • onServicesDiscovered()被调用
    调用mBluetoothGatt.discoverServices();方法后,onServicesDiscovered()这个方法会被调用,说明发现当前设备了。然后我们就可以在里面去获取BluetoothGattService和BluetoothGattCharacteristic。

    // BLE回调操作
    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
 
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState){
            super.onConnectionStateChange(gatt, status, newState);
 
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // 连接成功
 
                mBluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // 连接断开
               Log.d("TAG","onConnectionStateChange fail-->" + status);
            }
        }
 
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //发现设备,遍历服务,初始化特征
                initBLE(gatt);
            } else {
               Log.d("TAG","onServicesDiscovered fail-->" + status);
            }
        }
 
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status){
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // 收到的数据
                byte[] receiveByte = characteristic.getValue();
 
            }else{
               Log.d("TAG","onCharacteristicRead fail-->" + status);
            }
        }
 
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic){
            super.onCharacteristicChanged(gatt, characteristic);
            //当特征中value值发生改变
        }
 
        /**
         * 收到BLE终端写入数据回调
         * @param gatt
         * @param characteristic
         * @param status
         */
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
             // 发送成功
 
            } else {
             // 发送失败
            }
        }
 
        @Override
        public void onDescriptorWrite(BluetoothGatt gatt,
                                      BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
 
            }
        }
 
        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
 
            }
        }
 
        @Override
        public void onDescriptorRead(BluetoothGatt gatt,BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
 
            }
        }
    };

4. 获取特征

  • ble设备相关的UUID
    //写通道uuid
    private static final UUID writeCharactUuid = UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb");
    //通知通道 uuid
    private static final UUID notifyCharactUuid =UUID.fromString( "0000fff7-0000-1000-8000-00805f9b34fb");
  • 获取bluetoothGattCharacteristic(因为有的设备可能存在双服务的情况,所以这里遍历所有服务)
    //初始化特征
    public void initBLE(BluetoothGatt gatt) {
        if (gatt == null) {
            return;
        }
        //遍历所有服务
        for (BluetoothGattService BluetoothGattService : gatt.getServices()) {
            Log.e(TAG, "--->BluetoothGattService" + BluetoothGattService.getUuid().toString());
 
            //遍历所有特征
            for (BluetoothGattCharacteristic bluetoothGattCharacteristic : BluetoothGattService.getCharacteristics()) {
                Log.e("---->gattCharacteristic", bluetoothGattCharacteristic.getUuid().toString());
 
                String str = bluetoothGattCharacteristic.getUuid().toString();
                if (str.equals(writeCharactUuid)) {
                    //根据写UUID找到写特征
                    mBluetoothGattCharacteristic = bluetoothGattCharacteristic;
                } else if (str.equals(notifyCharactUuid)) {
                    //根据通知UUID找到通知特征
                    mBluetoothGattCharacteristicNotify = bluetoothGattCharacteristic;
                }
            }
        }
    }
  • 开启通知
    mBluetoothGatt.setCharacteristicNotification(mGattCharacteristicNotify, true);

5. 发送消息

    mGattCharacteristicWrite .setValue(sData);
    if (mBluetoothGatt != null) {
        mBluetoothGatt.setCharacteristicNotification(notifyCharactUuid , true);
        mBluetoothGatt.writeCharacteristic(mGattCharacteristicWrite );
    } 

6. 接收消息

 @Override
        public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status){
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // 收到的数据
                byte[] receiveByte = characteristic.getValue();
 
            }else{
               Log.d("TAG","onCharacteristicRead fail-->" + status);
            }
        }

7. 释放资源

    public boolean disConnect() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
            mBluetoothGatt = null;
            return true;
        }
        return false;
    }
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin-Dev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值