前言:
好几年前做了一个体温监测的手环App,前些日子给我反馈说很多客户都连不上设备了。我想是时候做安卓6.0+的适配了。
适配过程遇到各种问题,所以写下这编文章以记录。
最简单理解蓝牙:
主要几个东西:
BluetoothDevice
BluetoothGattService
BluetoothGattCharacteristic
BluetoothGattDescriptor
BluetoothGatt
1、BluetoothDevice下可能有多个BluetoothGattService,BluetoothGattService下可能有多个BluetoothGattCharacteristic,BluetoothGattCharacteristic下又可能有多个BluetoothGattDescriptor。它们都有一个UUID标识
2、连接成功后各种操作都用BluetoothGatt,获取BluetoothGattService等
主要流程:
1、判断是否支持BLE
2、判断权限
3、打开蓝牙并扫描设备
4、连接
5、读写和设置通知(如果设备主动发信息,可以通过notification的方式,否则要轮询地读设备数据)
6、关闭连接
下面我们都按这个流程来实现
1、判断是否支持BLE
@Override
public boolean isSupportBle() {
return mContext.getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
2、检查权限,如果是6.0+要加入
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
}
}
这里有坑,如果用户授权了定位权限,但是手机的位置信息没有打开也扫描不到设备,日志也没有看出任何问题。
检查是否打开位置信息:
public static boolean isLocationEnable(Context context) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean networkProvider = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
boolean gpsProvider = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (networkProvider || gpsProvider) {
return true;
}
return false;
}
跳到打开位置信息设置页面
private void setLocationService() {
try {
Intent locationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
this.startActivityForResult(locationIntent, REQUEST_CODE_LOCATION_SETTINGS);
}catch (Exception mE){
}
}
3、打开蓝牙并扫描设备
if (isSupportBle()) {
mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (null != mBluetoothAdapter) {
boolean enable = mBluetoothAdapter.enable();
}
}
扫描设备,是新版本中boolean startLeScan(LeScanCallback callback)已经过时用void startScan(final ScanCallback callback)替代。
@Override
public boolean scan(final ScanCallback listener) {
boolean success = false;
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 || null == mBluetoothManager) {
Log.e(TAG, "android.os.Build.VERSION.SDK_INT is lower than 18 or not support Ble");
} else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(new NewBaseScanCallback(listener));
Log.e(TAG, "bluetoothLeScanner.startScan");
success = true;
} else {
success = mBluetoothAdapter.startLeScan(new BaseScanCallback(listener));
Log.e(TAG, "bluetoothAdapter.startLeScan(callback)--");
}
return success;
}
4、连接设备
@Override
public BluetoothGatt connect(BluetoothDevice device, BluetoothGattCallback mGattCallback) {
mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
if (mBluetoothGatt == null) {
Log.e(TAG, "Trying to create a new connection.---false");
}
return mBluetoothGatt;
}
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
gatt.discoverServices();
updateState(IBle.STATE_CONNECTED);
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
updateState(IBle.STATE_DISCONNECTED);
} else if (newState == BluetoothGatt.STATE_CONNECTING) {
updateState(IBle.STATE_CONNECTING);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
updateState(IBle.STATE_SERVICES_DISCOVERED);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//进行接收数据的相关操作
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
if (null != mBleCallback) {
(mBleCallback).getData(data);
}
}
}
};
收到连接成功的回调后调用 gatt.discoverServices();还要等到void onServicesDiscovered(BluetoothGatt gatt, int status)回调的时候才可做操作。这时候才算真正意思的连接成功。
5、读写和设置通知
@Override
public boolean readAlertLevel(UUID service_uuid, UUID read_uuid) {
boolean status = false;
if (null == mBluetoothGatt) {
return status;
}
for (BluetoothGattService bluetoothGattService : mBluetoothGatt.getServices()) {
Log.e(TAG, "BluetoothGattService-writeLlsAlertLevel--mBluetoothGatt.getServices()--" + bluetoothGattService.getUuid());
}
BluetoothGattService linkLossService = mBluetoothGatt.getService(service_uuid);
if (linkLossService == null) {
Log.e(TAG, "mBluetoothGatt.getService(SERVIE_UUID)---link loss Alert service not found!");
return status;
}
BluetoothGattCharacteristic alertLevel = null;
alertLevel = linkLossService.getCharacteristic(read_uuid);
if (alertLevel == null) {
Log.e(TAG, "link loss Alert Level charateristic not found!");
return status;
}
status = mBluetoothGatt.readCharacteristic(alertLevel);
Log.e(TAG, "readLlsAlertLevel() - status=" + status);
return status;
}
@Override
public boolean writeAlertLevel(int iAlertLevel, UUID service_uuid, UUID write_uuid, byte[] bb) {
boolean status = false;
if (null == mBluetoothGatt) {
return status;
}
for (BluetoothGattService bluetoothGattService : mBluetoothGatt.getServices()) {
Log.e(TAG, "BluetoothGattService-writeLlsAlertLevel--mBluetoothGatt.getServices()--" + bluetoothGattService.getUuid());
}
BluetoothGattService linkLossService = mBluetoothGatt.getService(service_uuid);
if (linkLossService == null) {
Log.e(TAG, "mBluetoothGatt.getService(SERVIE_UUID)---link loss Alert service not found!");
return status;
}
BluetoothGattCharacteristic alertLevel = null;
alertLevel = linkLossService.getCharacteristic(write_uuid);
if (alertLevel == null) {
Log.e(TAG, "link loss Alert Level charateristic not found!");
return status;
}
int storedLevel = alertLevel.getWriteType();
Log.e(TAG, "storedLevel() - storedLevel=" + storedLevel);
alertLevel.setValue(bb);
Log.e("发送的指令", "bb" + bb[0]);
alertLevel.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
status = mBluetoothGatt.writeCharacteristic(alertLevel);
Log.e(TAG, "writeLlsAlertLevel() - status=" + status);
return status;
}
@Override
public boolean setNotify(UUID service_uuid, UUID read_uuid) {
if (null == getBluetoothGatt()) {
return false;
}
BluetoothGattService service = getBluetoothGatt().getService(service_uuid);
if (service == null) {
return false;
}
BluetoothGattCharacteristic characteristic = service.getCharacteristic(read_uuid);
if (null == characteristic) {
return false;
}
int properties = characteristic.getProperties();
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) {
Log.e(TAG, "Check characteristic property: false");
return false;
}
boolean success = getBluetoothGatt().setCharacteristicNotification(characteristic, true);
Log.e(TAG, "setCharacteristicNotification: "+success);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
getBluetoothGatt().writeDescriptor(descriptor);
}
return success;
}
6、关闭连接
@Override
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.e(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
mBluetoothGatt = null;
}
最后把扫描与连接的简单封装了一下提供了一个非常简单的扫描连接方法
/**
* 扫描设备自动连接
*
* @param timeout 超时时间,扫描不到自动停止
* @param mScanInterceptor 扫描到的设备判断是否符合条件,自动连接
*/
public void scanAndConnect(long timeout, ScanInterceptor mScanInterceptor)
demo代码https://github.com/YangKee/Ble
有不对的地方欢迎批评指正,谢谢