一、蓝牙4.0BLE的优势。

与传统蓝牙相比,蓝牙4.0低功耗,传输速度快,最多可连接27个设备,最大无线范围超过100米,反正就是各种好。

注意:安卓系统要求4.3及以上

二、安卓开发

(一) 扫描设备

注意:这是扫描的Activity,是通过控制界面的Activity的startActivityForResult()方法跳转过来的,功能是扫描设备并显示出来,点击设备时会返回对应设备地址和名称给另一个Activity。

1、扫描前要先判断手机是否支持BLE以及是否开启蓝牙


// 判断手机能不能用BLE
       if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            ToastUtil.showShortToast(mContext, R.string.ble_not_supported);
            finish();
        }

        // 初始化 Bluetooth adapter, 通过蓝牙管理器得到一个参考蓝牙适配器(API必须在以上android4.3或以上和版本)
        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // 检查设备上是否支持蓝牙
        if (mBluetoothAdapter == null) {
            ToastUtil.showShortToast(mContext, R.string.error_bluetooth_not_supported);
            finish();
            return;
        }

        //没开启蓝牙的话提示用户是否打开蓝牙
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

由于这里调用了startActivityForResult()方法,所以我们要重写onActivityResult()方法


@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 用户选择不打开蓝牙
        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
            finish();
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

2、开始扫描

进入Activity和扫描按钮按下都要用到,所以将代码抽取为一个方法。

private void scanLeDevice(final boolean enable) {
        if (enable) {
            // 10秒后停止扫描
            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 BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
<span style="white-space:pre">		</span>    // 添加扫描到的设备
mLeDeviceListAdapter.addDevice(device);
                }
            });
        }
    };

在ListView中显示扫描到的设备,并设置点击事件

mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
                if (device == null) return;
                mDeviceName = device.getName();
                mDeviceAddress = device.getAddress();
                if (mScanning) {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                    mScanning = false;
                }
<span style="white-space:pre">		</span>// finish()方法一执行,intent就发送回去了
                Intent intent = new Intent();
                intent.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
                setResult(Activity.RESULT_OK, intent);
                finish();
            }
        });



(二) 控制界面

1、上面说了,控制界面的Activity调用startActivityForResult()方法跳转到扫描界面获得设备地址,接下来就真正的连接了


@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case REQUEST_CONNECT_DEVICE:
                // 如果扫描界面有扫描到设备,则进行连接
                if (resultCode == Activity.RESULT_OK) {
                    mDeviceAddress = data.getExtras().getString(DeviceScanActivity.EXTRA_DEVICE_ADDRESS);
                    if (mBluetoothLeService != null) {
                        mBluetoothLeService.connect(mDeviceAddress);
                    }
                } else {
                    ToastUtil.showShortToast(mContext, "没有连接设备");
                }
                break;
        }
    }

2、如果连接成功,则进行回调,这部分是在BluetoothLeService类中,该类继承Service类,对相关操作进行封装,用来管理设备与手机的连接和数据传输。


private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            String intentAction;
            if (status == BluetoothGatt.GATT_SUCCESS) { //首先判断这个status,如果等于 BluetoothGatt.GATT_SUCCESS(value=0)代表这个回调是正常
                if (newState == BluetoothGatt.STATE_CONNECTED) {
                    intentAction = ACTION_GATT_CONNECTED;
                    mConnectionState = STATE_CONNECTED;
                    //连接成功的话马上查找服务
                    gatt.discoverServices();
                    broadcastUpdate(intentAction);

                } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
                    intentAction = ACTION_GATT_DISCONNECTED;
                    mConnectionState = STATE_DISCONNECTED;
                    gatt.discoverServices();
                    broadcastUpdate(intentAction);
                }
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            }
        }

        //从特征中读取数据
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

        //向特征中写入数据
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

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

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, descriptor.getCharacteristic());
        }

    };
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);
        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.对于所有的文件,写入十六进制格式的文件
        <span style="white-space:pre">	</span>//这里读取到数据
            final byte[] data = characteristic.getValue();
            for (int i = 0; i < data.length; i++) {
<span style="white-space:pre">		</span>System.out.println("data......" + data[i]);
<span style="white-space:pre">	</span>    }
            if (data != null && data.length > 0) {
                final StringBuilder stringBuilder = new StringBuilder(data.length);
                for(byte byteChar : data)
                <span style="white-space:pre">	</span>//以十六进制的形式输出
                    stringBuilder.append(String.format("%02X ", byteChar));
               // intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
                intent.putExtra(EXTRA_DATA, new String(data));
            }
        }
        //发送广播,广播接收器在控制界面的Activity
        sendBroadcast(intent);
    }

3、回到控制界面的Activity,在上面连接后的回调中发送了一条广播,而广播接收器在这里写,先注册广播,然后写接收到广播后对应的操作。

这里我只用到读和写,查阅《BLE4.0贴片模块手册》可得

读数据的服务和特征值的UUID分别是

READ_SERVICE = "0000ffe0-0000-1000-8000-00805f9b34fb";
READ_CHARACTERISTIC = "0000ffe4-0000-1000-8000-00805f9b34fb";

写数据的服务和特征值的UUID分别是

WRITE_SERVICE = "0000ffe5-0000-1000-8000-00805f9b34fb";
WRITE_CHARACTERISTIC = "0000ffe9-0000-1000-8000-00805f9b34fb";


// 注册广播
private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
        return intentFilter;
    }
</pre><pre name="code" class="java">// 广播接收器,处理服务所激发的各种事件
    // ACTION_GATT_CONNECTED: 连接一个GATT服务
    // ACTION_GATT_DISCONNECTED: 从GATT服务中断开连接
    // ACTION_GATT_SERVICES_DISCOVERED: 查找GATT服务
    // ACTION_DATA_AVAILABLE: 从服务中接受数据
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                mConnected = true;
                ToastUtil.showLongToast(mContext, "设备连接成功");
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                mConnected = false;
                ToastUtil.showLongToast(mContext, "设备连接已断开");
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // 读数据的服务和characteristic
                readMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString(READ_SERVICE));
                readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString(READ_CHARACTERISTIC));
                mBluetoothLeService.readCharacteristic(readCharacteristic);
                mBluetoothLeService.setCharacteristicNotification(readCharacteristic,true);
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                String data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
                displayData(data);
            }
        }
    };