Android干货android 开发程序员Android开发经验谈

Android BLE蓝牙详细解读(三)

2017-08-21  本文已影响499人  f2a928cacb8d

由于上半年公司发展迅猛,空闲时间有限,转眼已几个月未更新文章了,但是期间本人还是优化了一些蓝牙库中的接口,包括增加了OTA升级接口、连接超时提醒等接口、自定义注解代替之前的枚举(设备连接状态等)、解决了蓝牙管理类的连接设备数量错误BUG等等,接着上篇BLE蓝牙详细解读咱们继续,未看过的朋友可以先去参考Android BLE蓝牙详细解读(二)

下面主要分析本人蓝牙库中的蓝牙操作:

1、BluetoothLeService类是整个蓝牙的核心功能实现,BleManager是对外提供所有蓝牙操作接口的管理类,当BluetoothLeService处理之后要把结果返回到BleManager中,然后再由BleManager对外提供接口,他们之间通过handler进行连接起来,如下:

private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName,
                                   IBinder service) {
        mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
        if(mHandler != null){
            mBluetoothLeService.setHandler(mHandler);
        }
        Log.e(TAG, "Service connection successful");
        if (!mBluetoothLeService.initialize()) {
            Log.e(TAG, "Unable to initialize Bluetooth");       
            for (BleLisenter bleLisenter : mBleLisenters){
                bleLisenter.onInitFailed();
            }
        }
        if (mBluetoothLeService != null) mHandler.sendEmptyMessage(1);
        // Automatically connects to the device upon successful start-up
        // initialization.
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mBluetoothLeService = null;
    }
};

当BluetoothLeService中处理之后就会通知BleManager去处理状态改变,如下:

private Handler mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BleConfig.BleStatus.ConnectTimeOut:
                   ...
                  //此处略去详细代码(下同)
                break;
            case BleConfig.BleStatus.ConnectionChanged:
                   ...
                break;
            case BleConfig.BleStatus.Changed:
                   ...
                break;
            case BleConfig.BleStatus.Read:
                  ...
                break;
            case BleConfig.BleStatus.DescriptorWriter:
                    ...
                break;
            case BleConfig.BleStatus.OnReady:
                    ...
                break;
            case BleConfig.BleStatus.ServicesDiscovered:
                    ...
                break;
            case BleConfig.BleStatus.DescriptorRead:
                    ...
                break;
        }
    }
};

2、BleLisenter中是所有的接口的一个集合,在这里把它定义为一个抽象类,因为考虑到有部分回调方法是可以不用必须实现的(如果各位有更好的方式可以留言提示,不胜感激)。

3、在此要注意一些细节,比如大多数设备扫描的时候会重复扫描到相同蓝牙设备,必须要进行过滤,开发应用时,必须还要进行产品过滤,比如通过设备的广播包过滤,或者通过设备名过滤都是可以的,如下(注意:要根据自己产品提供的广播包进行过滤,下图是我们自己产品的):

  /**
 * Verify the product broadcast parameters
 * @param data Parameter data
 * @return Whether the match
 */
public static boolean matchProduct(byte[] data) {
    if (data == null || data.length <= 0) {
        return false;
    }
    int i = 0;
    do {
        // Read packet size
        int len = data[i++] & 0xff;
        if (len > 0) {
            // Read packet data
            byte[] d = new byte[len];
            int j = 0;
            do {
                d[j++] = data[i++];
            } while (j < len);
            // Authentication Type and Length
            if (d.length > BROADCAST_SPECIFIC_PRODUCT.length && (d[0] & 0xFF) == BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA) {
                // Matching product parameters
                boolean passed = true;
                for (int x = 0; x < BROADCAST_SPECIFIC_PRODUCT.length; x++) {
                    passed = passed && d[x + 1] == BROADCAST_SPECIFIC_PRODUCT[x];
                }
                //Match successful
                if (passed) {
                    return true;
                }
            }
        }

    } while (i < data.length);
    return false;
}

还有更重要的一个细节就是,在设备添加、移除或者连接状态发生改变时怎么样判断当前蓝牙对象是否是同一个对象(有点拗口),看下图:

 T device = null;
 try {
      device = mBleFactory.create(BleManager.this,(BluetoothDevice) msg.obj);
      }catch (Exception e) {
      e.printStackTrace();
  }

可以看到mBleFactory.create(BleManager.this,(BluetoothDevice) msg.obj)返回了一个蓝牙对象,该方法最基本的实现如下:

public T getBleDevice(BluetoothDevice device) {
    if (device == null) {
        return null;
    }
    synchronized (mConnetedDevices){
        if(mConnetedDevices.size() > 0){
            for (T bleDevice : mConnetedDevices) {
                if (bleDevice.getBleAddress().equals(device.getAddress())) {
                    return bleDevice;
                }
            }
        }
        T newDevice = (T) new BleDevice(device);
        return newDevice;
    }
}

这里就是判断当前已连接的蓝牙设备的集合中是否存在该设备,如果有直接返回该对象,如果没有则新创建一个蓝牙对象。

4、当设备连接成功之后并不代表就可以在此时发送数据,因为此时只是连接成功,并没有获取到蓝牙服务。必须要先通过gatt对象去获取服务discoverServices()在可以。如果想让APP内能够实时监听到蓝牙设备发来的数据,则还需要设置一个通知(可以理解成注册监听吧)如下:

@Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mHandler.obtainMessage(BleConfig.BleStatus.ServicesDiscovered, gatt).sendToTarget();
            //清空通知特性列表
            mNotifyCharacteristics.clear();
            mNotifyIndex = 0;
            //开始设置通知特性
            displayGattServices(gatt.getDevice().getAddress(), getSupportedGattServices(gatt.getDevice().getAddress()));
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }

5、该库中提供了简单的蓝牙自定义对象,并且用到的蓝牙设备是基于此自定义对象,如果想扩展更多蓝牙属性可以去继承并扩展它,所以为了更好的处理蓝牙对象的类型,库中定义了泛型类型。

新更新的OTA升级模块的接口还没有介绍,大家感兴趣可以去下载源码自己去查看,OK,要注意的细节问题已经介绍的差不多了,如果感兴趣的朋友可以大胆的去应用该库到自己的项目中。下篇准备更新一篇蓝牙3.0的文章(基于蓝牙音箱A2DP协议实现),感兴趣可以继续关注。
附DEMO下载地址

对BLE蓝牙感兴趣的朋友可以加入我们讨论群:

QQ:494309361(Android蓝牙开发小纵队)

上一篇下一篇

猜你喜欢

热点阅读