主要内容概况

前面我记录了开发蓝牙2.0的过程,今天准备介绍现在的主流蓝牙技术,蓝牙4.0的开发,该蓝牙技术是在Android4.3(API级别18)版本引入的API。

具体的区别主要以下几点:

1.新的蓝牙技术提供了连接服务的方法,以前是没有提供连接蓝牙的方法的。

2.扫描和连接设备,通过回掉的方式来进行通知,不再是以前通过系统广播的方式。

3.低功耗说明使用4.0的蓝牙能降低电量消耗。

4.数据交互的方式也不再是通过stock的方式,而是通过GATT协议。

开始介绍主要的开发内容

开发过程主要还是参考谷歌的官方案例,samples很简单主要就四个类。

官方连接

下面开始正式上代码

  1. 常规的,开启蓝牙,权限添加,设置设备可见等都不介绍了,可以看一下我以前那一篇文章。
    我们从设备扫描开始介绍,代码如下:
//获取一下蓝牙适配器
final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
//开始进行扫描设备了,传递进去一个监听
 mBluetoothAdapter.startLeScan(mLeScanCallback);
//再回掉里去接收扫描到的设备
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //这里要注意,会扫描到相同的设备,需要对列表进行去重复
                            mLeDeviceListAdapter.addDevice(device);
                            mLeDeviceListAdapter.notifyDataSetChanged();
                        }
                    });
                }
            };

//在add的方法中需要进行判断
 public void addDevice(BluetoothDevice device) {
            if (!mLeDevices.contains(device)) {
                mLeDevices.add(device);
            }
        }

以上基本上就可以完成扫描设备,并且显示刀列表上了。是不是很简单,连接设备部分会比较麻烦一些。

  1. 连接设备部分主要可以分为两个部分,一部分先是进行设备匹配;另一部分则是进行连接服务。

官方的demo上绑定一个服务,然后一些连接操作都放到服务中进行,实际我们可以直接调用连接设备的方法。

//连接设置,并传入一个连接的回掉。
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);
//6.0的设备中添加了新的参数,注解意思是首选连接的远程设备的类型,这里我们选择的是低功率蓝牙设备
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback,BluetoothDevice.TRANSPORT_LE);

然后主要的代码逻辑都是在我们的监听事件上,收到连接成功的监听,要去解析服务,这里要做的事情还有要查看链接的设备是否支持有对应的服务在BluetoothGatt里面。

下面这里上具体的代码,还是参照官方例子,稍微改进一下。

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                LogUtil.i("链接上");
               //连接上设备更新一下UI相关
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                LogUtil.i("断开链接");
                //断开设备也更行一下,如果需要重连接,可以在这里进行。
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                LogUtil.i("解析服务");
			//获取到支持的服务列表,下面代码块进行解析                               	 		
             displayGattServices(mBluetoothLeService.getSupportedGattServices());

            }

        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(characteristic);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(characteristic);
        }
    };
  1. 具体解析服务的地方主要是对设备的一个匹配,中心设备和外围设备有相同的UUID然后就可以识别上了。有一些常见的UUID可以了解一下。
//这里解析服务相关。主要是两个循环,一个是匹配服务的UUID,另外一个则是匹配特征的UUID,如果我们只匹配特定的设备,这里的UUID可以是一个固定的String(UUID)字符串。
for (BluetoothGattService gattService : gattServices) {
            HashMap<String, String> currentServiceData = new HashMap<String, String>();
           //获取每个服务的uuid
    		uuid = gattService.getUuid().toString();
            //匹配我们的uuid,只要不匹配的姐跳过继续匹配
    		if (!"某一个uuid".equals(s.getUuid().toString())) {
                continue;
            }

            // 上面服务匹配成功后,在匹配特征,其实和上面一样,拿到特定的uuid,匹配上后就可以发送数据了
            for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                uuid = gattCharacteristic.getUuid().toString();
                //匹配我们的uuid,只要不匹配的姐跳过继续匹配
    		if (!"某一个uuid".equals(s.getUuid().toString())) {
                continue;
            	}
                //发送特征通知,是否启用还特征设备通知。
                 setCharacteristicNotification(bc, true);
            }
        }

这里连接成功后,再把我们的具有我们指定特征的设备注册到远程的设备上去,这里我理解是给远程设备发放了一个通行证,这样你的数据才能发送到我(手机或中心设备)的上面。

/**
     * Enables or disables notification on a give characteristic.
     *
     * @param characteristic Characteristic to act on.
     * @param enabled If true, enable notification.  False otherwise.
     */
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        // This is specific to Heart Rate Measurement.
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            //构造出一个gatt的描述符号,注册到
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                    UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
           //Write the value of a given descriptor to the associated remote device.
           //这里就是类似的把描述符写到远程的设备上了。
            mBluetoothGatt.writeDescriptor(descriptor);
        }
    }
  1. 完成上面所有的步骤,基本上中心设备和远程设备已经可以进行数据的传递了。
    当发生数据变化的时候回去回掉我们最开始注册的mGattCallback方法中的实现。然后收到通知或者是read的消息的时候,获取到收到的数据。
//mGattCallback中的以下两个方法在发生数据变化的时候会被调用。然后我们在这里就可一拿到发送过来的数据了。
@Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(characteristic);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(characteristic);
        }
  1. 调用broadcastUpdate方法统一处理收到的通知或者数据,下面我们其实只关注自己指定的UUID设备就可以了,因为我们自己的应用也只是收集自己的远程设备的消息数据。
private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);

        // This is special handling for the Heart Rate Measurement profile.  Data parsing is
        // carried out as per profile specifications:
        // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            int flag = characteristic.getProperties();
            int format = -1;
            if ((flag & 0x01) != 0) {
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
                Log.d(TAG, "Heart rate format UINT16.");
            } else {
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
                Log.d(TAG, "Heart rate format UINT8.");
            }
            final int heartRate = characteristic.getIntValue(format, 1);
            Log.d(TAG, String.format("Received heart rate: %d", heartRate));
            intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
        } else {
            // For all other profiles, writes the data formatted in HEX.
            //对于其他的远程设备,我们不关心,这里可以不用实现
            }
        }
//这里我们直接拿到上面heartRate数据就可了。拿到数据后就可以回到我们正常的业务逻辑。
        sendBroadcast(intent);
    }

完成上述的几个大的步骤基本上你的远程设备和手机已经连接上了,总体来说整个过程还是比较容易。

蓝牙的内容也就到此结束了,如果有问题欢迎反馈给我,其实这个过程就是参考谷歌的官方demo来几率。当我们接触一个新的开发领域的时候,首先想到的应该就是跟着官方文档和demo走,防止自己误入歧途