zoukankan      html  css  js  c++  java
  • Android 蓝牙4.0 BLE (onServicesDiscovered 返回 status 是 129,133时)

    Android ble (Bluetooth Low Energy) 蓝牙4.0,也就是说android 4.3+, API level >= 18,且支持蓝牙4.0的手机才可以使用。

    BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,无需配对,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。

    官方demo:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html

    官方demo(csdn下载,感谢分享的人吧):http://download.csdn.net/detail/lqw770737185/8116019

    由于搜索需要尽量减少功耗,因此在实际使用时需要注意:
    1、当找到对应的设备后,立即停止扫描;
    2、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。
    注意:添加蓝牙权限:
    1 <uses-permission android:name="android.permission.BLUETOOTH"/>
    2 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    Android 6.0 及以上,还需添加位置权限
    如果6.0 这两个权限还是动态权限:
    1 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    2 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:
    1 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    设置required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature:
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
          //不支持ble
          finish();
    } 
     
    (1)实现 BluetoothAdapter.LeScanCallback接口,BLE设备的搜索结果将通过这个callback返回
     1       private ExecutorService mExecutor; 
     2 
     3       // Device scan callback.
     4       private BluetoothAdapter.LeScanCallback mLeScanCallback =
     5               new BluetoothAdapter.LeScanCallback() {
     6   
     7           @Override
     8           public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
     9                // 不能做太多事情,特别周围ble设备多的时候。容易出现错误或者ANR,需要把结果处理放到另外的线程去。
    10                if(mExecutor == null) {
    11                    mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
    12                }
    13                new ScanProcessor().executeOnExecutor(mExecutor, new ScanData(device, rssi, scanRecord));
    14          }
    15  
    16      };

    ScanData:

     1 private class ScanData {
     2     public ScanData(BluetoothDevice device, int rssi, byte[] scanRecord) {
     3         this.device = device;
     4         this.rssi = rssi;
     5         this.scanRecord = scanRecord;
     6     }
     7 
     8     int rssi;
     9     BluetoothDevice device;
    10     byte[] scanRecord;
    11  }

    扫描结果处理:

     1 private class ScanProcessor extends AsyncTask<ScanData, Void, MingbikeBeacon> {
     2 
     3         public ScanProcessor() {
     4         }
     5 
     6         @Override
     7         protected Object doInBackground(ScanData... params) {
     8             if (params == null || params.length == 0)
     9                 return null;
    10             ScanData scanData = params[0];
    11             // todo scanData
    12             return new Object();
    13         }
    14 
    15         @Override
    16         protected void onPostExecute(Object obj) {
    17              // todo obj
    18         }
    19 
    20         @Override
    21         protected void onPreExecute() {
    22         }
    23 
    24         @Override
    25         protected void onProgressUpdate(Void... values) {
    26         }
    27     }

    (2)关闭BLE设备的搜索

    1 mBluetoothAdapter.stopLeScan(mLeScanCallback);

    (3)开启BLE设备的搜索

    1 mBluetoothAdapter.startLeScan(mLeScanCallback);

    *************注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索。

    (4)使用BluetoothGatt 来连接设备

    两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server。
    连接GATT Server,你需要调用BluetoothDevice的connectGatt()方法。此函数带三个参数:Context、autoConnect(boolean)和BluetoothGattCallback对象。
    1 private BluetoothGatt mBluetoothGatt;
     1   final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); // 蓝牙mac地址
     2     if (device == null) {
     3       Log.w(TAG, "Device not found.  Unable to connect.");
     4       return false;
     5     }
     6     mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

    BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback);

    参数autoConnect,如果为 true 的话,系统就会发起一个后台连接,等到系统发现了一个设备,就会自动连上,通常这个过程是非常慢的。为 false 的话,就会直接连接,通常会比较快。同样,BluetoothGatt.connect()只能发起一个后台连接,不是直接连接。所以这个地方需要小心。

    (5)使用BluetoothGattCallback 来跟设备进行通信

     1 private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
     2     @Override
     3     public void onConnectionStateChange(BluetoothGatt gatt, 
     4         int status, int newState) {
     5         super.onConnectionStateChange(gatt, status, newState); 
    9
    if (newState == BluetoothProfile.STATE_CONNECTED) { 10 setState(State.CONNECTED); 11 gatt.discoverServices(); 12 } else { 13 setState(State.IDLE); 14 } 15 } 16 17 @Override 18 public void onServicesDiscovered(BluetoothGatt gatt, 19    int status) { 20 if(status == BluetoothGatt.GATT_SUCCESS) { 21 Log.v(TAG, "onServicesDiscovered: " + status); 22 } 23 } 24 }

    连接时会走这个方法onConnectionStateChange,传过来的新状态是连接状态,这时在这个方法中调用一下这句:mBluetoothGatt.discoverServices(),

    mBluetoothGatt是连接完成时的对象,调用这句后,会走回调函数的onServicesDiscovered方法。在这个方法中去获取设备的一些服务,蓝牙通道,然后通过这些通道去发送数据给外设。

    有些设备,在 onServicesDiscovered 回调中,返回的 status 是 129,133时,在关闭,重新打开蓝牙,无法解决问题时,建议更换设备,这些问题大多是设备底层gatt 服务异常,重新连接,进行discoverServices();

    1 // 出现129,133时。关闭蓝牙
    2 mBluetoothAdapter.disable();
    3 // 关闭蓝牙后,延时1s,重新开启蓝牙
    4 mBluetoothAdapter.enable();

    在蓝牙设备中, 其包含有多个BluetoothGattService, 而每个BluetoothGattService中又包含有多个BluetoothGattCharacteristic。

    当onServicesDiscovered()回调的 status == BluetoothGatt.GATT_SUCCESS, 可以进行获取service。

    (1)获取到设备中的服务列表  mBluetoothGatt.getServices(); 或者通过uuid 来获取某一个服务:

    public List<BluetoothGattService> getSupportedGattServices() {
            if (mBluetoothGatt == null)
                return null;
            return mBluetoothGatt.getServices();
            // 或者通过uuid来获取某一个服务 mBluetoothGatt.getServices(uuid);
    }

    (2)通过Gatt这个对像,就是蓝牙连接完成后获取到的对象,通过这个对象设置好指定的通道向设备中写入和读取数据。

    在onServicesDiscovered(BluetoothGatt gatt, int status) 回调中, 获取服务对象,获取读取、写入、描述的特征。

     1  // 获取指定uuid的服务
     2  BluetoothGattService service = gatt.getService(BluetoothUUID.bleServerUUID);
     3  // 获取读取特征
     4  BluetoothGattCharacteristic readCharacteristic = service.getCharacteristic(BluetoothUUID.readDataUUID);
     5  // 获取写入特征
     6  writeCharacteristic = service.getCharacteristic(BluetoothUUID.writeDataUUID);
     7  gatt.setCharacteristicNotification(readCharacteristic, true);
     8  // 获取描述特征
     9  BluetoothGattDescriptor descriptor = readCharacteristic.getDescriptor(BluetoothUUID.CLIENT_CHARACTERISTIC_CONFIG);
    10  // 要看外围设备的设置
    11  if (isNotify) {
    12     descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    13  } else {
    14     descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
    15  }
    16  gatt.writeDescriptor(descriptor);

    服务中, 也可以通过调用 gattService.getCharacteristics():获得Characteristic 集合.

    在 Characteristic中, 可以通过  mBluetoothGatt.readCharacteristic(characteristic); 来读取其里面的数据, 其结果在mGattCallback 回调函数中获取.

    写如数据:  

    mCurrentcharacteristic.setValue(data); 

    mBluetoothGatt.wirteCharacteristic(mCurrentcharacteristic);

    1 public void wirteCharacteristic(BluetoothGattCharacteristic characteristic) {
    2 
    3         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
    4             Log.w(TAG, "BluetoothAdapter not initialized");
    5             return;
    6         }
    7 
    8         mBluetoothGatt.writeCharacteristic(characteristic);
    9  }
    1 public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    2         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
    3             Log.w(TAG, "BluetoothAdapter not initialized");
    4             return;
    5         }
    6         mBluetoothGatt.readCharacteristic(characteristic);
    7  }

    当外围设备,调用:

    BluetoothGattServer.class
    notifyCharacteristicChanged(BluetoothDevice device,
    BluetoothGattCharacteristic characteristic, boolean confirm);

    就会触发中心设备的onCharractersticChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) 方法回调:

    1 @Override
    2 public void onCharacteristicChanged(BluetoothGatt gatt,
    3                                             BluetoothGattCharacteristic characteristic) {
    4      byte[] values = characteristic.getValue();
    5      // todo values
    6 }

    注意:BluetoothGattCharacteristic这个是指定的通道,蓝牙服务:

    1 BluetoothGattCharacteristic characteristic = null;
    2 characteristic = mGattCharacteristics.get(4).get(4);

    这两个数字就是从指定的服务中找到你要发送数据的那个服务。

    如果要进行多个连接,每次连接完成后可以将BluetoothGatt的对象放到一个list里面,获取到的服务也放到一个List里面,然后发送数据的时候调用不同的Gatt发送不同的通道数据即可。

    参考:

    Android提高之Android手机与BLE终端通信

    android 蓝牙4.0多通道

    Android Bluetooth Low Energy(官方)

  • 相关阅读:
    CSS3实现投影效果
    @font-face使用在线字体
    JS全局对象的属性
    const命令声明变量应注意的几点
    IDEA设置不区分大小写提示
    分布式ID生成-雪花算法
    项目Git分支管理规范
    IDEA使用Mybatis插件 MyBatisCodeHelper-Pro
    解决码云出现git@gitee.com: Permission denied (publickey).
    安装RabbitMQ,一直提示Erlang版本过低
  • 原文地址:https://www.cnblogs.com/CharlesGrant/p/5033428.html
Copyright © 2011-2022 走看看