• 关于Android 5.x的低功耗蓝牙BLE开发简介


      蓝牙4.0分为标准蓝牙和低功耗蓝牙(BLE),标准蓝牙就是手机上用的那种,低功能耗蓝牙由于其具有最大化的待机时间、快速连接和低峰值的发送和接收特性,被广泛用于智能手表、智能手环等可穿戴设备上。在安卓4.3之前,安卓平台上的BLE开发相当难搞,好在谷歌在4.3之后发布了官方的API。在安卓5.0之后又引入了新的API,原来的API已经被废弃。在新的系统里采用旧API开发的APP仍可使用,但采用新API开发的APP只能在LOLLIPOP即安卓5.0及其以后的版本使用。

      标准蓝牙的的开发和BLE不同。标准蓝牙连接里有两个角色一个是客户端一个是服务器,当客户端搜索到蓝牙服务器后并与之配对后,才能通过UUID(这个是唯一的,服务器端必须与客户端一致)建立socket,然后使用流像文件读写和网络通信那样传输数据就行了。在BLE里,变成了中心设备(central)和外围设备(peripheral),中心设备就是你的手机,外围设备就是智能手环一类的东西。开发BLE的应用都得遵守Generic Attribute Profile (GATT),一个BLE蓝牙设备包含多个service,每个service又包含多个characteristic。每个characteristic有一个value和多个descriptor,通过characteristic中心设备与外围设备进行通信。descriptor顾名思义,包含了BLE设备的一些信息。不同service、characteristic和descriptor都有各自己唯一的UUID。想要跟BLE设备通信,首先通过UUID获取目标服务,然后再通过UUID获取characteristic,charateristic起着载体的作用,通过writeCharacteristic()和readCharacteristic(),可以写入和读出信息。每个characteristic都有一些自己的属性,其中在property里,说明了该characteristic的属性,例如READ|WRITE|WRITE_NO_RESPONSE|NOTIFY。

      以下是关键代码:

      1.首先,在AndroidManifest.xml里加入  

       <uses-feature android:name="android.bluetooth.le" android:required="true"/> //Android 5.0之前是android.hardware.bluetooth_le
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
        <uses-permission android:name="android.permission.BLUETOOTH"/>

      使用新API时,最好在调用方法前判断下系统版本,否则会出现意想不到的错误。

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      ...
    }

      2.获取BluetoothAdapter。

     BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);//这里与标准蓝牙略有不同
     BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
      /*隐式打开蓝牙*/
      if (!bluetoothAdapter.isEnabled()) {
        bluetoothAdapter.enable();
      }

      3.然后获取BluetoothLeScanner,通过startScan()方法扫描周围的BLE设备。当搜索到目标设备或者搜索一定时间后通过BluetoothScanner的stopScan()方法停止搜索。  

    BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
    scanner.startScan(leCallback);

       leCallback是一个回调函数,通过onScanResult()把每次搜索到的设备添加到本地。leCallback还有一个方法onBatchScanResults(),按理说应该是批量处理搜索到的结果,但是不知道为什么在我这里无法调用。  

    leCallback = new ScanCallback() {
                    @Override
                    public void onScanResult(int callbackType, ScanResult result) {
                        super.onScanResult(callbackType, result);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            BluetoothDevice device = result.getDevice();
                            if (!devices.contains(device)) {  //判断是否已经添加
                                devices.add(device);
                            }
                            deviceAdapter.notifyDataSetChanged();
                        }
                    }
    
                    @Override
                    public void onScanFailed(int errorCode) {
                        super.onScanFailed(errorCode);
                        Log.e("搜索失败");
                    }
                };

      4.开始连接。

    BluetoothGatt bluetoothGatt = device.connectGatt(MainActivity.this, false, new BluetoothGattCallback() {
    
                    @Override
                    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                        if (newState == BluetoothProfile.STATE_CONNECTED) {   
                            gatt.discoverServices();          //连接成功,开始搜索服务,一定要调用此方法,否则获取不到服务
                        }
                    }
    
                    @Override
                    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                          Log.e(TAG,gatt.getDevice().getName() + " write successfully");
                    }
    
                    @Override
                    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                          String value = UtilOnStr.parseBytesToHexString(characteristic.getValue());
                            Log.e(TAG,gatt.getDevice().getName() + " recieved " + value);
                    }
    
            @Override
              public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                    String response = UtilOnStr.parseBytesToHexString(characteristic.getValue());
                 Log.e(TAG,  "The response is "+ response); 
                }
    
    });

      

       5.写入和读取数据。

    BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
    if(service != null) {
      characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
      if (characteristic !=null) {    
        bluetoothGatt.setCharacteristicNotification(characteristic, true);  //设置characteristic的通知,触发bluetoothGatt.onCharacteristicWrite()事件。
            characteristic.setValue(UtilOnStr.parseHexStringToBytes(value));
            bluetoothGatt.writeCharacteristic(characteristic);  
        }
    }

      当连接上BLE设备后,调用discoveryServices()发现服务,通过SERVICE_UUID获取目标service,如果service不为空,再通过CHARACTERISTIC_UUID获取characteristic,借助characteristic写入指定值与BLE设备进行通信。这里要注意的是characteristic接收的是一个byte数组,而且读写的方法都是异步的。调用bluetoothGatt.readCharacteristic(characteristic)可读取BLE设备返回的值。bluetoothGatt的writeCharacteristic()方法会触发BluetoothGattCallback里的onCharacteristicWrite(),相应的,bluetoothGatt的readCharacteristic()方法会触发onCharacteristicRead()。

      对中心设备与外围设备的传输数据的处理发生在onCharacteristicChanged()里,当characteristic写入了正确的数值后,会激活BLE设备,不时地返回数据。

      当不知道BLE设备的service和characteristic的对应的UUID时,可以在回调函数里的onServicesDiscovered()方法里,通过BluetoothGatt的getServices()获得该设备所有的service,然后再调用service的getUuid()得到其UUID,同理通过service的getCharacteristics()可以得到每个service所有的characteristic和其UUID。然后一个个不厌其烦的尝试,找到正确的service和characteristic(我就是这样干的)。

      6.总结

      由于网上关于Android 5.x 低功耗蓝牙 BLE开发的资料不是很多,我才下决心写这篇文章的,希望能对做相关开发犯过迷糊的人有一点儿帮助。同时我也是个安卓开发的新手,对蓝牙串口通信也是一知半解,表述难免有纰漏,理解也不到位,如果有幸被大家看到这篇文章我会倍感欣慰,并希望能指出不足之处。

      

      

  • 相关阅读:
    15.Git版本控制系统
    14.sudo 获取root权限
    13.定时任务
    12.文件权限:RWX
    相信301跳转大家都知道 rewrite
    修改mysql密码
    mod_rewrite是Apache的一个非常强大的功能
    mysql 常用命令集锦[绝对精华]
    mysql安装完之后,登陆后发现只有两个数据库
    mysql命令
  • 原文地址:https://www.cnblogs.com/witchiman/p/5320671.html
Copyright © 2020-2023  润新知