Android开发并非我擅长,最近刚好又做回蓝牙业务了,iOS搞完了看了下Android的,似乎区别不大,唯一比iOS多了一个descriptor
,那就顺便把Android蓝牙也搞搞吧,本文仅做学习记录完成过程以及中途遇到的坑。代码是否规范不重要哈!
1.申请权限
首先在第一步我就遇到了坑,Android蓝牙开发需要申请地理位置权限,位置权限是敏感权限,高版本Android需要动态申请,最开始我只在AndroidManifest
添加了权限申请,结果就是不管用旧的API还是最新的API扫描,都不走扫描结果回调!注意避坑!!!
在AndroidManifest
中申请以下权限
然后在代码中动态申请,这里写一个requestPermission
方法申请
private void requestPermission() {
//动态申请
if (Build.VERSION.SDK_INT < 23) {
return;
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}
2.初始化蓝牙
private void initBluetooth() {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (mBluetoothAdapter == null) {
Log.d(TAG, "蓝牙不支持");
} else {
int status = mBluetoothAdapter.getState();
if (status == BluetoothAdapter.STATE_OFF) {
mBluetoothAdapter.enable();
}else {
Log.d(TAG, "蓝牙可用");
}
}
}
3.开始扫描
扫描的方式可以用BluetoothAdapter.startLeScan
扫描,但谷歌已经不建议用该方法扫描了,那就用最新的BluetoothLeScanner
的方式扫描吧,在需要扫描的地方添加下面的代码,扫描可以根据服务UUID扫描固定一类蓝牙设备,也可以不过滤扫描所有设备,我这里根据服务UUID过滤(Android除了UUID过滤扫描,还有其他的,比如name等,没仔细研究,iOS过滤条件目前就只有UUID)
List bleScanFilters = new ArrayList<>();
bleScanFilters.add(new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("你的蓝牙设备UUID")).build());
ScanSettings scanSetting = new ScanSettings.Builder().setScanMode(SCAN_MODE_LOW_LATENCY).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).setMatchMode(ScanSettings.MATCH_MODE_STICKY).build();
mBluetoothLeScanner.startScan(bleScanFilters, scanSetting, callback);
Log.d(TAG, "开始扫描");
4.扫描回调并连接
因为我是根据UUID只扫描一类设备,为了省电扫描到后我直接停止扫描,然后连接第一个扫描到的设备
final ScanCallback stopCallback = new ScanCallback() {
};
final ScanCallback callback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.d(TAG, "扫描到设备");
BluetoothDevice btDevice = result.getDevice();
mBluetoothLeScanner.stopScan(stopCallback);
btDevice.connectGatt(MainActivity.this,true, connectCallBack);
}
@Override
public void onBatchScanResults(List results) {
super.onBatchScanResults(results);
Log.d(TAG, "onBatchScanResults");
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d(TAG, "扫描出错");
}
};
5.连接回调,发现服务,发现特征值,监听蓝牙发过来的数据,发送数据
final BluetoothGattCallback connectCallBack = new BluetoothGattCallback(){
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED){
Log.d(TAG, "连接成功");
gatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.d(TAG, "发现服务");
BluetoothGattService service = gatt.getService(UUID.fromString("蓝牙设备服务uuid"));
if (service != null){
Log.d(TAG, "有服务");
BluetoothGattCharacteristic notyChara = service.getCharacteristic(UUID.fromString("蓝牙设备可监听征值uuid"));
if (notyChara != null){
Log.d(TAG, "有特征值");
boolean success = gatt.setCharacteristicNotification(notyChara,true);
Log.i("监听结果: "+success);
BluetoothGattDescriptor descriptor = notyChara.getDescriptor(UUID.fromString("蓝牙设备描述uuid"));
if (descriptor != null){
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
//发送数据
BluetoothGattCharacteristic writeChar = service.getCharacteristic(UUID.fromString("蓝牙设备可写特征值的UUID"));
writeChar.setValue(TAG.getBytes());
gatt.writeCharacteristic(writeChar);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d(TAG, "收到数据");
}
};
值得一提的是,为了省电,在不需要监听数据后需要设置停止监听,也可以用readCharacteristic
读取单条数据
gatt.setCharacteristicNotification(chara,false);
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
在不需要蓝牙后应该调用gatt的disconnect
方法断开设备,因为有最大连接数限制,还需要调用gatt的close
方法释放当前连接个数计数。