最近公司项目要通过手机蓝牙连接硬件设备,硬件设备的通讯方式采用的是BLE低功耗蓝牙,低功耗蓝牙与传统用蓝牙的区别现在不做赘述,BLE在Android4.3以上系统开始提供支持,且目前大部分的蓝牙通讯模式采用的都是低功耗蓝牙,下面是我再开发中了解到额一些知识,在这里做基础总结,如果有不对和欠妥的地方请指正。原谅小弟的学疏才浅
1、权限声明
特别注意蓝牙搜索需要位置权限
<!--声明蓝牙权限-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
1 获取蓝牙是适配器
蓝牙适配器 BluetoothAdapter在此可以看做是搜索蓝牙设备的工具获得方式:
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3开始搜索蓝牙设备
设计到一个搜索回调
BluetoothAdapter.LeScanCallback leScanCallback;
//蓝牙连接返回的对象
BluetoothGatt bluetoothGatt;
//搜索回调
leScanCallback = new BluetoothAdapter.LeScanCallback() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
//停止搜索到条件 比如我们搜索到名字叫xx的设备就停止搜索
if (!TextUtils.isEmpty(device.getName()) && "xx".equals(device.getName().trim())) {
if (adapter.isDiscovering()) {
adapter.stopLeScan(leScanCallback);
}
//蓝牙连接
bluetoothGatt = device.connectGatt(ConnectActivivty.this, false, bluetoothGattCallback);
}
}
};
BluetoothGatt 是个非常重要的类,它由蓝牙连接成功后返回,连接成功的后得读写操作都要靠他来完成,所以一般把他定义成全局变量
我们连接获取BluetoothGatt 之后就要进行服务端连接,我在这里是这么认为的,蓝牙设备连接成功之后现在还不能进行通讯,BluetoothGatt 下包含很多服务也就是 BluetoothGattService,而每个BluetoothGattService下面又有很多蓝牙通道BluetoothGattCharacteristic,我们只有确定了蓝牙传输通道,在这个通道内才可以进行通讯。
上面的
bluetoothGattCallback就是设备连接时候要设置的回调
//连接的回调
bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
//这里是子线程你不要做UI操作,否侧程序无法继续执行
//连接成功
//Toast.makeText(ConnectActivivty.this, "连接成功", Toast.LENGTH_LONG).show();
//去发现服务
bluetoothGatt.discoverServices();
}
}
//发现服务
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//我们通过服务id来获取唯一服务,这个一般由硬件厂商指定,当然它也有一个默认值
BluetoothGattService bluetoothGattService = bluetoothGatt.getService(UUID.fromString("00001000-0000-1000-8000-00805f9b34fb"));
//我们通过特征(也就是我们说的通道)id来获取唯一读取特征,这个一般由硬件厂商指定,当然它也有一个默认值
BluetoothGattCharacteristic characteristic = bluetoothGattService.getCharacteristic(UUID.fromString("00001002-0000-1000-8000-00805f9b34fb"));
拿到通道后我们设置监听此通道
gatt.setCharacteristicNotification(characteristic, true);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.e("hhh", new String(characteristic.getValue()));
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
}
//特征值发生变化,我们认为有新的消息发来
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e("hhh", new String(characteristic.getValue()));
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
}
};
以下是在真正进入蓝牙连接之前索要做的零基础判断,包括是否支持BLE,蓝牙和定位是否开启,权限是否开启
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
ToastUtils.showShort("您的手机不支持此功能");
return;
}
boolean isBlueToothEnable = isBlueToothEnable();
boolean isLocalEnable = isLocationEnabled();
if (!isBlueToothEnable && isLocalEnable) {
ToastUtils.showShort("请打开蓝牙功能");
return;
}
if (isBlueToothEnable && !isLocalEnable) {
ToastUtils.showShort("请打开定位功能");
return;
}
if (!isBlueToothEnable && !isLocalEnable) {
ToastUtils.showShort("请打开蓝牙和定位功能");
return;
}
XXPermissions.with(ChooseCheckItemActivity.this)
.constantRequest()
.permission(new ArrayList(){{
add(Manifest.permission.ACCESS_COARSE_LOCATION);
add(Manifest.permission.BLUETOOTH);
add(Manifest.permission.ACCESS_FINE_LOCATION);
add(Manifest.permission.BLUETOOTH_ADMIN);}})
.request(new OnPermission() {
@Override
public void hasPermission(List<String> granted, boolean isAll) {
if (isAll) {
if (ChooseCheckItemActivity.this != null) {
//前往蓝牙连接
Intent intent = new Intent(ChooseCheckItemActivity.this, CheckBloodActivity.class);
startActivity(intent);
finish();
}
ALog.dTag("", "获取权限成功");
} else {
ALog.dTag("", "获取权限成功,部分权限未正常授予");
}
}
@Override
public void noPermission(List<String> denied, boolean quick) {
if (quick) {
ALog.dTag("", "被永久拒绝授权,请手动授予权限");
//如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.gotoPermissionSettings(ChooseCheckItemActivity.this);
} else {
ALog.dTag("", "获取权限失败");
}
}
});
}
});
//蓝牙是否打开
public boolean isBlueToothEnable() {
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter
.getDefaultAdapter();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
return true;
}
return false;
}
//定位手机定位功能是否打开
public boolean isLocationEnabled() {
int locationMode = 0;
String locationProviders;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
return false;
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
} else {
locationProviders = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}