浅谈蓝牙4.0 Ble开发

蓝牙4.0是2012年最新蓝牙版本,是3.0的升级版本;较3.0版本更省电、成本低、3毫秒低 延迟、超长有效连接距离、 AES-128加密等;通常用在蓝牙耳机、蓝牙音箱等设备上。
蓝牙技术联盟(Bluetooth SIG)2010年7月7日宣布,正式采纳蓝牙4.0核心规范(Bluetooth Core Specification Version 4.0 ),并启动对应的认证计划。会员厂商可以提交其产品进行测试,通过后将获得蓝牙4.0标准认证。 该技术拥有极低的运行和待机功耗,使用一粒 纽扣电池甚至可连续工作数年之久。
作为新版蓝牙,有其一下特点:
速度:支持1Mbps数据传输率下的超短 数据包,最少8个八组位,最多27个。所有连接都使用蓝牙2.1加入的减速呼吸模式(sniff subrating)来达到超低工作循环。
跳频:使用所有蓝牙规范版本通用的自适应 跳频,最大程度地减少和其他2.4GHz ISM频段无线技术的串扰。
主控制:更加智能,可以休眠更长时间,只在需要执行动作的时候才唤醒。
延迟:最短可在3毫秒内完成连接设置并开始传输数据。
范围:提高调制指数,最大范围可超过100米(根据不同应用领域, 距离不同)。
健壮性:所有数据包都使用24- bit CRC校验,确保最大程度抵御干扰。
安全:使用AES-128 CCM加密算法进行数据包加密和认证。
拓扑:每个数据包的每次接收都使用32位寻址,理论上可连接数十亿设备;针对一对一连接优化,并支持星形拓扑的一对多连接;使用快速连接和断开,数据可以再网状拓扑内转移而无需维持复杂的 网状网络
蓝牙4.0开启还和传统蓝牙一样,先 获取BluetoothAdapter   
final BluetoothManager bluetoothManager =
                (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

然后调用BluetoothAdapter的enable方法开启蓝牙,此方法开启在高版本安卓系统上回弹出一个对话框让用户选择是否开启,需要用户确认,还有一种方法可以直接跳到设置中去开启,这里就不详细讲

 if (!mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.enable();
        }

接下来是扫描设备,调用的是startLeScan方法,和传统蓝牙有点不一样,停止扫描是stopLeScan,这两个方法都有一个参数BlutoothAdapter.LeScanCallback,这是一个扫描回调类,扫描结果都是通过这个类回调。通常情况下扫描需要给一个时间限制,不然就有点浪费资源,一般是设置10秒的样子

    public void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }
public interface LeScanCallback {
    void onLeScan(BluetoothDevice var1, int var2, byte[] var3);
}

接下来上扫描的全部代码,自己封装的一个类

public class DeviceScanManager {

    private static final String TAG = "DeviceScanManager";
    public static final int MSG_BLUTOOTH_TURNING_ON = 1;
    public static final int MSG_BLUTOOTH_ON = 2;
    public static final int MSG_BLUTOOTH_TURNING_OFF = 3;
    public static final int MSG_BLUTOOTH_OFF = 4;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    private BluetoothAdapter.LeScanCallback mLeScanCallback;

    public static DeviceScanManager instance() {
        return Builder.manager;
    }

    private DeviceScanManager() {
    }

    static class Builder {
        public static final DeviceScanManager manager = new DeviceScanManager();
    }

    public boolean isScanning() {
        return mScanning;
    }

    public DeviceScanManager registerReceiver(Context context) {
        // 注册蓝牙好、状态监听
        context.registerReceiver(mReceiver,makeFilter());
        return this;
    }

    public void unregisterReceiver(Context context) {
        context.unregisterReceiver(mReceiver);
    }

    public DeviceScanManager initBletooth(@Nonnull Context context, Handler handler, BluetoothAdapter.LeScanCallback mLeScanCallback) {

        if (handler == null) {
            mHandler = new Handler();
        } else {
            mHandler = handler;
        }

        if (mLeScanCallback != null) {
            this.mLeScanCallback = mLeScanCallback;
        }

        // Use this check to determine whether BLE is supported on the device.  Then you can
        // selectively disable BLE-related features.
        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            ToastUtil.showToast(context, R.string.ble_not_supported);
            return this;
        }

        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
        // BluetoothAdapter through BluetoothManager.
        final BluetoothManager bluetoothManager =
                (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            ToastUtil.showToast(context, R.string.error_bluetooth_not_supported);
            return this;
        }
        return this;
    }
    private IntentFilter makeFilter() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        return filter;
    }

    public DeviceScanManager checkBluetoothEnable() {
        if (!mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.enable();
        }
        return this;
    }

    public void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            LogUtil.i(TAG, "onReceive---------");

            switch (intent.getAction()) {
                case BluetoothAdapter.ACTION_STATE_CHANGED:
                    int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
                    Message msg = mHandler.obtainMessage();
                    switch (blueState) {
                        case BluetoothAdapter.STATE_TURNING_ON:
                            LogUtil.i("onReceive---------STATE_TURNING_ON");
//                            msg.what = MSG_BLUTOOTH_TURNING_ON;
                            break;
                        case BluetoothAdapter.STATE_ON:
                            LogUtil.i("onReceive---------STATE_ON");
                            msg.what = MSG_BLUTOOTH_ON;
                            break;
                        case BluetoothAdapter.STATE_TURNING_OFF:
                            LogUtil.i("onReceive---------STATE_TURNING_OFF");
//                            msg.what = MSG_BLUTOOTH_TURNING_OFF;
                            break;
                        case BluetoothAdapter.STATE_OFF:
                            LogUtil.i("onReceive---------STATE_OFF");
                            msg.what = MSG_BLUTOOTH_OFF;
                            break;
                    }
                    msg.sendToTarget();
                    break;
            }
        }
    };
}

扫描到设备之后就是连接,通过传入设备的mac地址进行连接

final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

获取到远程设备之后三个参数分别为上下文,是否自动连接,还有一个回调,是用来接收ble的数据的

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        // 连接状态改变
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                LogUtil.i(TAG, "Connected to GATT server.");
                // Attempts to discover services after successful connection.
                LogUtil.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                LogUtil.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
            }
        }
        //发现服务
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                LogUtil.w(TAG, "onServicesDiscovered received: " + status);
            }
        }
        // 服务读取特征回调
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
        //特征改变,通常情况下是设置了通知从这里回调
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    };

接下来说说,蓝牙4.0数据读取,ble的数据读写都是通过服务来进行,onServicesDiscovered方法会把发现的服务都回调,

服务有分很多种,可以通过服务的UUID区分,UUID在厂商开发硬件时就会知道,并且知道他们的功能

 @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                LogUtil.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

每个服务下面都有一些特征这些特征提供了读、写、收数据的一些方法,一般分为:可读,专门用来读取数据;可写,专门用来写数据;可通知,是用来设置收到消息时发送通知回调;

调用readCharacteristic方法读取数据,蓝牙收到就会回调onCharacteristicRead传回数据

 mBluetoothGatt.readCharacteristic(characteristic);

BluetoothGattCharacteristic可以通过以下方法获取,要读就传入可读的UUID,要写就传入可写的UUID

 BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(GattAttributes.READ_UUID));
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

调用writeCharacteristic方法写数据

mBluetoothGatt.writeCharacteristic(characteristic);
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

特别注意的是ble因为是低功耗蓝牙,每次能传输的数据只有20个字节,能接受到的 数据每次也只能20个,所以需要进行数据分段发送,如果出现每次发送数据无反应的情况下,就需要每次发送数据都延迟一定的时间,我这里是延时了10毫秒,具体根据情况来定

 public synchronized void writeCharacteristic(byte[] value, int len) {
        if (gattService == null || mBluetoothLeService == null) {
            return;
        }
        LogUtil.it(TAG, "write value to ble length :" + len + " value :" + byte2hex(value, len, true));
        // 将数据长度超过20的分段发送
        int outCount = len % 20 > 0 ? len / 20 + 1 : len / 20;
        BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(GattAttributes.WRITE_UUID));

        for (int i = 0; i < outCount; i++) {
            int innerCount = len > 20 ? 20 : len;
            byte[] temp = new byte[innerCount];
            for (int j = 0; j < innerCount; j++) {
                temp[j] = value[i * 20 + j];
            }
            LogUtil.it(TAG, "write value to ble innerCount :" + innerCount + " value :" + byte2hex(temp, innerCount, true));
            characteristic.setValue(0, 17, 0);
            characteristic.setValue(temp);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mBluetoothLeService.writeCharacteristic(characteristic);
            len -= 20;
        }

    }
然后是开启设备回调通知 setCharacteristicNotification 这个方法可以开启设备的通知,当有信息从蓝牙返回时就会通过通知返回,并回调
public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
    
    
 public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }

        boolean isEnableNotification =  mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        if(isEnableNotification) {
            List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
            if(descriptorList != null && descriptorList.size() > 0) {
                for(BluetoothGattDescriptor descriptor : descriptorList) {
                    if (enabled) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    } else {
                        descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
                    }
                    mBluetoothGatt.writeDescriptor(descriptor);
                }
            }
        }
    }

这里有一个需要注意的点,有时候会碰上设置了通知没有回调的情况,这里就要注意了,可能是通知没有设置到位,这里使用的方法是遍历设置法,将每一个BlutoothGattDescriptor设置一遍

boolean isEnableNotification =  mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        if(isEnableNotification) {
            List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
            if(descriptorList != null && descriptorList.size() > 0) {
                for(BluetoothGattDescriptor descriptor : descriptorList) {
                    if (enabled) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    } else {
                        descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
                    }
                    mBluetoothGatt.writeDescriptor(descriptor);
                }
            }
        }

接下来上完整代码,分别封装了两个完整类分别是核心服务BletoothLeServicce 和DeviceControlManager类,前一个是整个服务的核心,后一个是承担了连接和读写的功能

public class BletoothLeService extends Service {
    public final static String TAG = BletoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.idutex.alonevehicle.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.idutex.alonevehicle.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.idutex.alonevehicle.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.idutex.alonevehicle.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA_STRING =
            "com.idutex.alonevehicle.le.EXTRA_DATA_STRING";
    public final static String EXTRA_DATA_BYTE =
            "com.idutex.alonevehicle.le.EXTRA_DATA_BYTE";


    // Implements bletoothReadCallback methods for GATT events that the app cares about.  For example,
    // connection change and services discovered.
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {


        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                LogUtil.i(TAG, "Connected to GATT server.");
                // Attempts to discover services after successful connection.
                LogUtil.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                LogUtil.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                LogUtil.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

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

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    };

    public boolean isBleConnected() {
        return mConnectionState == STATE_CONNECTED;
    }

    private void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);
        Bundle bundle = new Bundle();
        bundle.putByteArray(EXTRA_DATA_BYTE, characteristic.getValue());
        LogUtil.it(TAG,"Service receive data " + DeviceControlManager.byte2hex(characteristic.getValue(),0,true));
        // 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 (GattAttributes.WRITE_UUID.equals(characteristic.getUuid().toString())) {
            int flag = characteristic.getProperties();
            int format = -1;
            if ((flag & 0x01) != 0) {
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
                LogUtil.d(TAG, "Heart rate format UINT16.");
            } else {
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
                LogUtil.d(TAG, "Heart rate format UINT8.");
            }
            final int heartRate = characteristic.getIntValue(format, 1);
            LogUtil.d(TAG, String.format("Received heart rate: %d", heartRate));
            bundle.putString(EXTRA_DATA_STRING, String.valueOf(heartRate));
        } else {
            // For all other profiles, writes the data formatted in HEX.
            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
                final StringBuilder stringBuilder = new StringBuilder(data.length);
                for(byte byteChar : data)
                    stringBuilder.append(String.format("%02X ", byteChar));
                bundle.putString(EXTRA_DATA_STRING, new String(data) + "\n" + stringBuilder.toString());
            }
        }
        intent.putExtras(bundle);
        sendBroadcast(intent);
    }

    public class LocalBinder extends Binder {
        BletoothLeService getService() {
            return BletoothLeService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        close();
        return super.onUnbind(intent);
    }

    private final IBinder mBinder = new LocalBinder();

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                LogUtil.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            LogUtil.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }

        return true;
    }

    /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     *
     * @param address The device address of the destination device.
     *
     * @return Return true if the connection is initiated successfully. The connection result
     *         is reported asynchronously through the
     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     *         callback.
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // Previously connected device.  Try to reconnect.
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            LogUtil.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            LogUtil.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        LogUtil.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    /**
     * Disconnects an existing connection or cancel a pending connection. The disconnection result
     * is reported asynchronously through the
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     * callback.
     */
    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }

    /**
     * After using a given BLE device, the app must call this method to ensure resources are
     * released properly.
     */
    public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

    /**
     * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
     * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
     * callback.
     *
     * @param characteristic The characteristic to read from.
     */
    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

    public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

    /**
     * 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) {
            LogUtil.w(TAG, "BluetoothAdapter not initialized");
            return;
        }

        boolean isEnableNotification =  mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        if(isEnableNotification) {
            List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
            if(descriptorList != null && descriptorList.size() > 0) {
                for(BluetoothGattDescriptor descriptor : descriptorList) {
                    if (enabled) {
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    } else {
                        descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
                    }
                    mBluetoothGatt.writeDescriptor(descriptor);
                }
            }
        }
    }

    /**
     * Retrieves a list of supported GATT services on the connected device. This should be
     * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
     *
     * @return A {@code List} of supported services.
     */
    public List<BluetoothGattService> getSupportedGattServices() {
        if (mBluetoothGatt == null) return null;

        return mBluetoothGatt.getServices();
    }
}
public class DeviceControlManager {

    private static final String TAG = "DeviceControlManager";
    private static final int MSG_RECEIVE_DATA = 0x01;
    private BletoothLeService mBluetoothLeService;
    private String mDeviceAddress;
    private BluetoothGattService gattService;
    BletoothReadCallback bletoothReadCallback;              // 用于接收数据的回调
    BletoothStateListener bleStateListener;
    int index = 0;                              // 每次拷贝开始的指针
    private byte[] bytes = new byte[2];         // 拼接的最终数据

    private final ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((BletoothLeService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                LogUtil.e(TAG, "Unable to initialize Bluetooth");
            }
            // Automatically connects to the device upon successful start-up initialization.
            mBluetoothLeService.connect(mDeviceAddress);
        }

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

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == MSG_RECEIVE_DATA) {
                // 收到消息之后分发出去
                sendReceiveData(msg.getData());
            }
            return false;
        }
    });

    public void setBletoothReadCallback(BletoothReadCallback bletoothReadCallback) {
        this.bletoothReadCallback = bletoothReadCallback;
    }

    interface BletoothReadCallback {
        void receive(byte[] result);
    }

    public interface BletoothStateListener {
        void connected();

        void disconnect();
    }

    public void setBleStateListener(BletoothStateListener bleStateListener) {
        this.bleStateListener = bleStateListener;
    }

    private void sendReceiveData(Bundle extras) {
        try {
            byte[] buff = extras.getByteArray(BletoothLeService.EXTRA_DATA_BYTE);

            if (buff == null) {
                LogUtil.it(TAG, "BLE receive data byte array is null");
                return;
            }

            LogUtil.it(TAG, "BLE receive data " + byte2hex(buff, 0,true));
            if ((buff[0] & 0xff) == 0xaa && (buff[1] & 0xff) == 0x55 && buff.length > 3) {
                byte[] dst = new byte[2];
                System.arraycopy(buff, 2, dst, 0, 2);
                String strLen = byte2hex(dst, 2);
                int len = Integer.valueOf(strLen, 16);
                LogUtil.it(TAG, "len   " + len + ",strlen   " + strLen);
                bytes = new byte[len + 8];
            }

            if (bytes != null) {
                int copyLen = buff.length + index >= bytes.length ? bytes.length - index : buff.length;
                System.arraycopy(buff, 0, bytes, index, copyLen);
                index = index + copyLen;
                if (index == bytes.length) {
                    index = 0;
                    if (bletoothReadCallback != null) {
                        bletoothReadCallback.receive(bytes);
                        bytes = new byte[2];
                    }
                }
            }
        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

    private DeviceControlManager() {
    }

    public DeviceControlManager registerReceiver(Context context) {
        context.registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
        return this;
    }

    public DeviceControlManager unregisterReceiver(Context context) {
        context.unregisterReceiver(mGattUpdateReceiver);
        return this;
    }

    public static DeviceControlManager instance() {
        synchronized (DeviceControlManager.class) {
            return Builder.manager;
        }
    }

    static class Builder {
        public static final DeviceControlManager manager = new DeviceControlManager();
    }

    public DeviceControlManager bindBleService(Context context) {
        Intent gattServiceIntent = new Intent(context, BletoothLeService.class);
        context.bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
        return this;
    }

    public void unbindBleService(Context context) {
        context.unbindService(mServiceConnection);
        mBluetoothLeService = null;
    }

    public boolean connectDevice(String address) {
        mDeviceAddress = address;
        if (mBluetoothLeService != null) {
            final boolean result = mBluetoothLeService.connect(mDeviceAddress);
            LogUtil.d(TAG, "Connect request result=" + result);
            return result;
        }
        return false;
    }

    public DeviceControlManager disconnectDevice() {
        if (mBluetoothLeService != null) {
            mBluetoothLeService.disconnect();
        }
        return this;
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BletoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BletoothLeService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BletoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BletoothLeService.ACTION_DATA_AVAILABLE);
        return intentFilter;
    }

    public List<BluetoothGattService> getSupportedGattServices() {
        if (mBluetoothLeService != null) {
            return mBluetoothLeService.getSupportedGattServices();
        }
        return null;
    }

    public void readCharacteristic() {
        if (gattService == null || mBluetoothLeService == null) {
            LogUtil.it(BletoothLeService.TAG, "gattService is null");
            return;
        }

        BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(GattAttributes.WRITE_UUID));
        mBluetoothLeService.readCharacteristic(characteristic);
    }

    private void setCharacteristicNotification(boolean enabled) {
        if (gattService == null || mBluetoothLeService == null) {
            return;
        }

        BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(GattAttributes.NOTIFY_UUID));
        mBluetoothLeService.setCharacteristicNotification(characteristic, enabled);
    }

    public synchronized void writeCharacteristic(byte[] value, int len) {
        if (gattService == null || mBluetoothLeService == null) {
            return;
        }
        LogUtil.it(TAG, "write value to ble length :" + len + " value :" + byte2hex(value, len, true));
        // 将数据长度超过20的分段发送
        int outCount = len % 20 > 0 ? len / 20 + 1 : len / 20;
        BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString(GattAttributes.WRITE_UUID));

        for (int i = 0; i < outCount; i++) {
            int innerCount = len > 20 ? 20 : len;
            byte[] temp = new byte[innerCount];
            for (int j = 0; j < innerCount; j++) {
                temp[j] = value[i * 20 + j];
            }
            LogUtil.it(TAG, "write value to ble innerCount :" + innerCount + " value :" + byte2hex(temp, innerCount, true));
            characteristic.setValue(0, 17, 0);
            characteristic.setValue(temp);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mBluetoothLeService.writeCharacteristic(characteristic);
            len -= 20;
        }

    }

    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(final Context context, Intent intent) {
            final String action = intent.getAction();
            if (BletoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                LogUtil.it(BletoothLeService.TAG, "Ble Connected");
                RunEnvironmentSetting.bluetoothConnection = true;
                if (bleStateListener != null) bleStateListener.connected();
            } else if (BletoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                LogUtil.it(BletoothLeService.TAG, "Ble Disconnected");
                RunEnvironmentSetting.bluetoothConnection = false;
                if (bleStateListener != null) bleStateListener.disconnect();
            } else if (BletoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                LogUtil.it(BletoothLeService.TAG, "Ble Find services");
                // Show all the supported services and characteristics on the user interface.
                List<BluetoothGattService> gattServices = mBluetoothLeService.getSupportedGattServices();
                for (BluetoothGattService service :
                        gattServices) {
                    if (service.getUuid().toString().equals(GattAttributes.SERVICE_UUID)) {
                        gattService = service;
                        LogUtil.it(BletoothLeService.TAG, "Ble get service");
                        // 设置自动接收通知数据
                        setCharacteristicNotification(true);
                    }
                }
            } else if (BletoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                Bundle extras = intent.getExtras();
                if (extras == null) {
                    LogUtil.it(TAG, "BLE receive data is null");
                    return;
                }

                Message msg = handler.obtainMessage();
                msg.what = MSG_RECEIVE_DATA;
                msg.setData(extras);
                msg.sendToTarget();
            }
        }
    };

    /**
     * 字节数组转换为十六进制字符串
     *
     * @param b byte[] 需要转换的字节数组
     * @return String 十六进制字符串
     */
    public static String byte2hex(byte b[], int len, boolean log) {
        int length = len;
        if (b == null) {
            throw new IllegalArgumentException(
                    "Argument b ( byte array ) is null! ");
        }

        if (length == 0) {
            length = b.length;
        }
        StringBuilder sb = new StringBuilder();
        String stmp = "";

        for (int i = 0; i < length; i++) {
            stmp = Integer.toHexString(b[i] & 0xff);
            if (stmp.length() == 1) {
                sb.append("0").append(stmp);
            } else {
                sb.append(stmp);
            }
            if (log) {
                sb.append(" ");
            }
        }
        return sb.toString().toUpperCase();
    }

    public static String byte2hex(byte b[], int len) {
        return byte2hex(b, len, false);
    }

    /**
     * 十六进制串转化为byte数组
     *
     * @return the array of byte
     */
    public static byte[] hex2byte(String hex)
            throws IllegalArgumentException {
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException();
        }
        char[] arr = hex.toCharArray();
        byte[] b = new byte[hex.length() / 2];
        for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
            String swap = "" + arr[i++] + arr[i];
            int byteint = Integer.parseInt(swap, 16) & 0xFF;
            b[j] = Integer.valueOf(byteint).byteValue();
        }
        return b;
    }
}

然后蓝牙4.0有一个很不一样的情况,返回数据通常是异步的,这就让我们不能去主动获取数据带来一些不便,也有可能你们遇上的情况刚好就是要异步,我个人想了一个办法将被动收取数据换成主动收取,使用的是阻塞队列,阻塞队列是一个很好用的队列,可以让开发者不用去考虑数据传输时候的同步异步过程,详情使用可以去参考其他文章,这里不多说,自己封装了一个类BleBlockingQueue,可以在数据过多的情况下,字型添加队列,以房租收到数据过多时产生数据丢失,但是这里最好设置其空间的大小,不然可能会导致手机空间不足,这里限制了3个,每个队列的容量设置为了30个,超过3个就会将以前的清除掉

public class BleBlockingQueue {

    private static final String TAG = "BleBlockingQueue";
    public static final int TYPE_LINKEDBLOCKINGQUEUE = 0;
    public static final int TYPE_SYNCHRONOUSQUEUE = 1;
    // 每个队列的容量
    private static final int SINGLE_CAPACITY = 30;
    private LinkedList<LinkedBlockingQueue<CommData>> blockingQueues;
    private LinkedBlockingQueue<CommData> mSaveQueue;
    private LinkedBlockingQueue<CommData> mPollQueue;
    private SynchronousQueue<CommData> synchronousQueue;
    private int mType = 0;

    public BleBlockingQueue(int type) {
        mType = type;
        if (mType == TYPE_LINKEDBLOCKINGQUEUE) {
            blockingQueues = new LinkedList<>();
            mSaveQueue = addNewQueue();
            mPollQueue = blockingQueues.getFirst();
        } else if (mType == TYPE_SYNCHRONOUSQUEUE) {
            // 默认采用公平锁
            synchronousQueue = new SynchronousQueue<>(true);
        }
    }

    public CommData take() throws InterruptedException {
        if (mType == TYPE_LINKEDBLOCKINGQUEUE) {
            synchronized (BleBlockingQueue.class) {
                if (mPollQueue.isEmpty() && blockingQueues.size() > 1) {
                    blockingQueues.removeFirst();
                    mPollQueue = blockingQueues.getFirst();
                }
            }
            LogUtil.it(TAG, "BlockingQueues size\t" + blockingQueues.size() + ",mPollQueue size\t" + mPollQueue.size());
            return mPollQueue.poll(5, TimeUnit.SECONDS);
        } else if (mType == TYPE_SYNCHRONOUSQUEUE) {
            return synchronousQueue.poll(5, TimeUnit.SECONDS);
        }

        return null;
    }

    public boolean put(CommData data) throws InterruptedException {
        if (mType == TYPE_LINKEDBLOCKINGQUEUE) {
            boolean offer = mSaveQueue.offer(data, 1, TimeUnit.SECONDS);
            if (!offer) {
                mSaveQueue = addNewQueue();
                offer = mSaveQueue.offer(data, 1, TimeUnit.SECONDS);
            }
            return offer;
        } else if (mType == TYPE_SYNCHRONOUSQUEUE) {
            synchronousQueue.offer(data,1, TimeUnit.SECONDS);
        }

        return false;
    }

    private synchronized LinkedBlockingQueue<CommData> addNewQueue() {
        if (blockingQueues.size() >= 3) {
            // 最多允许存3个队列,防止用户手机内存爆
            blockingQueues.removeLast();
        }
        LogUtil.it(TAG, "The queue is full,addNewQueue");
        LinkedBlockingQueue<CommData> queue;
        queue = new LinkedBlockingQueue<>(SINGLE_CAPACITY);
        blockingQueues.add(queue);

        return blockingQueues.getLast();
    }

    public void clear() {
        if (mType == TYPE_LINKEDBLOCKINGQUEUE) {
            mPollQueue.clear();
        }  // else do nothing
    }
}

上面那个类的原理就是先将从蓝牙收到的数据存入队列,然要要用的时候再一一取出,为了达到一一对应的效果,最好是在每次写过数据之后都去读一遍,或者每次写数据之前都将队列清空,这样就可以达到被动数据变成主动获取的情况。


以上就是所有的蓝牙4.0相关,鄙人知道的就这么多和大家一起分享,不喜勿喷










猜你喜欢

转载自blog.csdn.net/wh2526422/article/details/80712080