蓝牙4.0技术相比较传统的蓝牙技术,具有传输速度快,低功耗,范围更广等优点,在开发过程中,相较于2.0,蓝牙4.0开发不需要进行输入配对吗,可以直接进行手机和模块的配对,而且可以实现一对多的连接,在我看来,蓝牙4.0在开发过程中与传统的蓝牙开发的最大不同是蓝牙4.0涉及到多通道连接,当用户进行手机和模块的连接的过程中,用户需要进行蓝牙通道的选择,不过对于开发者来说,我们可以在开发的过程中将通道进行锁死,这样的好处是可以在用户不知道该选择哪个通道的时候直接替用户选择好,用户直接进行连接就可以,但是这么做会有一定的纰漏,那就是需要涉及到蓝牙模块的批次,当进行订购的时候,需要订购相同通道号的蓝牙,所以,在开发的过程中是选择锁死通道还是让用户选择通道这个需要根据具体的使用情况进行筛选,这里我所讲的蓝牙4.0为单片机的蓝牙,对于音频(如耳机)这样的不涉及。
蓝牙BLE4.0主要又Service,Characteristic,Descriptor三个部分组成,一个BLE4.0里包含了多个service,一个service中又包含了多个characteristic,一个characteristic又会包含多个descriptor,其中在开发的过程中,characteristic是我感觉最重要的部分,这里涉及到蓝牙通信中的连接,收发等主要部分,可以说characteristics是BLE4.0开发的核心,关于characteristic谷歌官方在帮助文档上给出了具体的开发步骤,详细代码如下:
public class BluetoothLeService extends Service { //判断是否数据发送结束 public boolean isBleSendFinish = true; //蓝牙管理者 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.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA"; //数据 public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); public final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED;//已经连接 broadcastUpdate(intentAction); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; broadcastUpdate(intentAction); } } //发现服务 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w("123", "received:" + status); } } //从通道中读取 @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); isBleSendFinish = true; if (status == BluetoothGatt.GATT_SUCCESS) { //写入数据成功 } } //当通道发生改变的时候 @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } }; 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; } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; } final int hearRate = characteristic.getIntValue(format, 1); intent.putExtra(EXTRA_DATA, String.valueOf(hearRate)); } else { intent.putExtra(EXTRA_DATA, characteristic.getValue()); } sendBroadcast(intent); } public class LocaBinder extends Binder { public BluetoothLeService getService() { return BluetoothLeService.this; } } @Nullable @Override public IBinder onBind(Intent intent) { return null; } private final IBinder mBinder = new LocaBinder(); @Override public boolean onUnbind(Intent intent) { close(); return super.onUnbind(intent); } public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; } public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { return false; } return mBluetoothGatt.readCharacteristic(characteristic); } //新增 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { //没有初始化 return false; } return mBluetoothGatt.writeCharacteristic(characteristic);//characteristic } //设置通道的通知 public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { return; } mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); if (descriptor != null) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } } public boolean initialize() { //初始化 if (mBluetoothManager == null) { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { return false; } } mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { return false; } return true; } public boolean connect(final String address) { if (mBluetoothAdapter == null || address == null) { return false; } if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { if (mBluetoothGatt.connect()) { mConnectionState = STATE_CONNECTING;//连接中的状态 return true; } else { return false; } } final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);//得到蓝牙的设备 if (device == null) { return false; } mBluetoothGatt = device.connectGatt(this, false, mGattCallback); mBluetoothDeviceAddress = address; mConnectionState = STATE_CONNECTING; return true; } public void disconnect() {//断开连接 if (mBluetoothAdapter == null || mBluetoothGatt == null) { return; } mBluetoothGatt.disconnect(); } public List<BluetoothGattService> getSupportedGattServices() { if (mBluetoothGatt == null) { return null; } return mBluetoothGatt.getServices(); } }
在具体的连接方式中,所用到的俩捏步骤和蓝牙2.0的连接步骤大致相同,这里就不在多说。以为之前讲过,在开发的过程中为了方便用户,我会将通道进行锁死,这样连接之后,用户可以不用选择通道从而进行数据的收发,为了实现这一个效果,我会做一个封装类,下面为封装类的代码:
public class FEShare { public Intent intent = new Intent(); public Context context; public ArrayList<BluetoothDeviceDetail> deviceDetails = new ArrayList<BluetoothDeviceDetail>(); public ArrayList<String> BLE_device_addrs = new ArrayList<String>(); public BLE_item ble_item = new BLE_item(); public BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); public int connect_state = BluetoothProfile.STATE_DISCONNECTED; public BluetoothDevice device; //BLE public BluetoothLeService bluetoothLeService; public BluetoothAdapter.LeScanCallback leScanCallback; public ScanCallback scanCallback; public boolean isBLEWriteWithResponse = true; private Handler mHandler = new Handler() { }; private Runnable stopBLEScanRunnable; private IntentFilter intentFilter; public final ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { bluetoothLeService = ((BluetoothLeService.LocaBinder) iBinder).getService(); if (!bluetoothLeService.initialize()) { Log.e("封装类", "没有初始化蓝牙"); } bluetoothLeService.connect(device.getAddress()); } @Override public void onServiceDisconnected(ComponentName componentName) { bluetoothLeService = null; } }; /****************************************************/ private static class FEShareHolder { static final FEShare INSTANCE = new FEShare(); } public static FEShare getInstance() { return FEShareHolder.INSTANCE; } private FEShare() { } private void setupRunnable() { if (stopBLEScanRunnable == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { stopBLEScanRunnable = new Runnable() { @Override public void run() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && scanCallback != null) { bluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && leScanCallback != null) { bluetoothAdapter.stopLeScan(leScanCallback); } } }; } } //扫描停止 synchronized public boolean stopSearch() { if (stopBLEScanRunnable != null) { mHandler.removeCallbacks(stopBLEScanRunnable); return stopSearch(); } return false; } private boolean scan() { if (bluetoothAdapter == null) { return false; } setupRunnable(); if (stopSearch()) { if (deviceDetails != null) { deviceDetails.clear(); BLE_device_addrs.clear(); } return BLEScan(); } return false; } private boolean BLEScan() { if (stopBLEScanRunnable == null) return false; mHandler.postDelayed(stopBLEScanRunnable, 1000); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && scanCallback != null) { bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback); return true; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && leScanCallback != null) { return bluetoothAdapter.startLeScan(leScanCallback); } return false; } synchronized public boolean connect(BluetoothDevice device) { return BLEConnect(device); } private boolean BLEConnect(BluetoothDevice device) { this.device = device; return bluetoothLeService.connect(device.getAddress()); } private void disConnect() { new Thread(new Runnable() { @Override public void run() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { bluetoothLeService.disconnect(); } } }).start(); } public int write(final byte[] b) { if (ble_item.write_characteristic == null) return 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {//判断系统版本 int packets = 0; int lenth_bytes = b.length; final int perPacketLength = 20;//设置包的长度 if (lenth_bytes > perPacketLength) { int startPoint = 0; byte[] bytes = new byte[perPacketLength]; while (lenth_bytes > perPacketLength) { while (!bluetoothLeService.isBleSendFinish) {//当发送数据没有结束的时候 } bluetoothLeService.isBleSendFinish = false; ble_item.write_characteristic.setValue(bytes); startPoint += perPacketLength;//当发送的数据启示 lenth_bytes -= perPacketLength; if (bluetoothLeService.writeCharacteristic(ble_item.write_characteristic)) { packets++; } } while (!bluetoothLeService.isBleSendFinish) { } bluetoothLeService.isBleSendFinish = false; if (lenth_bytes != perPacketLength) {//当包的大小不等于设定好的长度时 lenth_bytes = b.length % perPacketLength;//取余 } if (lenth_bytes > 0) { byte[] bytes_last = new byte[lenth_bytes]; if (isBLEWriteWithResponse) { ble_item.write_characteristic.setValue(bytes_last); packets = bluetoothLeService.writeCharacteristic(ble_item.write_characteristic) ? packets + 1 : packets; } else { ble_item.write_characteristic_NoRe.setValue(bytes_last); packets = bluetoothLeService.writeCharacteristic(ble_item.write_characteristic_NoRe) ? packets + 1 : packets; } } return packets; } else { if (isBLEWriteWithResponse) { ble_item.write_characteristic.setValue(b); return bluetoothLeService.writeCharacteristic(ble_item.write_characteristic) ? 1 : 0; } else { ble_item.write_characteristic_NoRe.setValue(b); return bluetoothLeService.writeCharacteristic(ble_item.write_characteristic_NoRe) ? 1 : 0; } } } return 0; } public int write(byte b[], int offset, int len) { byte by[] = new byte[len]; System.arraycopy(b, offset, by, 0, len); return write(by); } public int write(String string) { if (string.length() < 1) { return 0; } return write(string.getBytes()); } private Object readResolve() { return getInstance(); } public void init(Context context) { this.context = context; context.registerReceiver(receiver, intentFilter); } private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action) || BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { connect_state = BluetoothProfile.STATE_CONNECTED; } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) || BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { //断开连接后把写特征至空 } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { setServiceUUID(bluetoothLeService.getSupportedGattServices()); } } }; @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public void setServiceUUID(List<BluetoothGattService> services) { for (BluetoothGattService service : services) { } for (BluetoothGattService service : services) { for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { final int charaProp = characteristic.getProperties(); if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {//阅读 } if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {//通知 bluetoothLeService.setCharacteristicNotification(characteristic, true); } if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {//写入没响应 if (ble_item.write_characteristic_NoRe == null) { ble_item.write_characteristic_NoRe = characteristic; } } if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {//写 if (ble_item.write_characteristic == null) { ble_item.write_characteristic = characteristic; } } } } } //设备的属性 public void addDevice(BluetoothDeviceDetail deviceDetail) { deviceDetails.add(deviceDetail); } private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } class BLE_item { public ArrayList<String> arr_serviceUUID = new ArrayList<String>(); public ArrayList<BluetoothGattService> arr_services = new ArrayList<BluetoothGattService>(); public BluetoothGattCharacteristic write_characteristic;//写的通道 public BluetoothGattCharacteristic write_characteristic_NoRe;//无应答 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public void addService(BluetoothGattService service) { service.getCharacteristics(); arr_services.add(service); String str_uuid = service.getUuid().toString(); arr_serviceUUID.add(str_uuid.substring(4, 8)); ArrayList list = new ArrayList(); // 得到所有的服务通道 for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { String str_c_uuid = characteristic.getUuid().toString(); str_c_uuid = str_c_uuid.substring(4, 8); list.add(str_c_uuid); if (str_c_uuid.toLowerCase().contains("fff1")) { bluetoothLeService.setCharacteristicNotification(characteristic, true); } // toLowerCase将所有的字母变为小写 // contains判断是否有当前的字符串 if (str_c_uuid.toLowerCase().contains("2af1")) {//这个为选择 write_characteristic = characteristic; } } } } }之后在接下来的开发中,我们都要使用这个类来进行相应的功能实现,具体的可以搜我的GitHub: wenhao555,那里会有相对应地使用方法