Android 蓝牙开发
Android 蓝牙开发
我们在进行蓝牙开发时首先要了解低功耗蓝牙、经典蓝牙、单模蓝牙、双模蓝牙之间的关系。
单模蓝牙:支持低功耗蓝牙
双模蓝牙:支持低功耗蓝牙和经典蓝牙
经典蓝牙:支持经典蓝牙
蓝牙开发大致分为这几步
前面需要定位权限和蓝牙权限的支持
发现设备:
经典蓝牙发现设备api
BluetoothAdapter.startDiscovery
听说可以同时发现经典蓝牙和低功耗蓝牙(BLE)。但我这没单模的蓝牙,所以没办法测试。
因为startDiscovery返回的是一个是否开始扫描设备,我们要获取蓝牙设备的信息需要注册一个 BroadcastReceiver 针对 ACTION_FOUND
Intent 会携带额外的字段 EXTRA_DEVICE 和 EXTRA_CLASS。这两者分别包含 BluetoothDevice 和 BluetoothClass。
var filter =IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver,filter); val mReceiver = object :BroadcastReceiver(){ override fun onReceive(context: Context?, intent: Intent?) { var action = intent?.action if(BluetoothDevice.ACTION_FOUND == action){ var device = intent?.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) } } } override fun onDestroy() { super.onDestroy() unregisterReceiver(mReceiver) }
Ble发现设备api
BluetoothAdapter.getBluetoothLeScanner()
BluetoothLeScanner.startScan(ScanCallback callback)//扫描会在屏幕关闭时停止以节省电量。 再次打开屏幕时,将恢复扫描。 为避免这种情况,请使用下面两个
BluetoothLeScanner.startScan(List<ScanFilter> filters, ScanSettings settings, ScanCallback callback)
BluetoothLeScanner.startScan(List<ScanFilter> filters, ScanSettings settings, PendingIntent callbackIntent)
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); filters.add(new ScanFilter.Builder() .setServiceUuid(ParcelUuid.fromString("0000000-0000-0000-0000-000000000000")) .build()); bluetoothLeScanner.startScan(filters , new ScanSettings.Builder().build(), new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Log.d(MainActivity.ACTIVITY_SERVICE, "onScanResult:"+ "\n Rssi() "+result.getRssi()+ "\n AdvertisingSid() "+result.getAdvertisingSid()+ "\n DataStatus() "+result.getDataStatus()+ "\n ScanRecord() "+result.getScanRecord().getBytes()+ "\n Address() "+result.getDevice().getAddress()+ "\n Address() "+result.getDevice().getName() ); } } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } } );
建立连接:
在配对之前应该先cancelDiscovery()或者stopScan(callback),
然后根据获取到蓝牙Mac地址连接即可
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(result.getDevice().getAddress()); device.connectGatt(MainActivity.this, false, new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); //status 连接或断开连接操作成功 BluetoothGatt.GATT_SUCCESS(0) //newState 新的连接状态 BluetoothProfile.STATE_DISCONNECTED(0-断开状态) BluetoothProfile.STATE_CONNECTED(1-连接状态) } } );
数据通信:
ble写入操作
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); //status 连接或断开连接操作成功 BluetoothGatt.GATT_SUCCESS(0) //newState 新的连接状态 BluetoothProfile.STATE_DISCONNECTED(0-断开状态) BluetoothProfile.STATE_CONNECTED(1-连接状态) //获取远程设备提供的服务以及它们的特征和描述符。这步很重要而且是一个异步操作(这里需要时间直接接在下面写不可取) bluetoothGatt.discoverServices(); //获取蓝牙GATT服务 BluetoothGattService gattService = bluetoothGatt.getService(UUID.fromString("00000000-0000-0000-0000-000000000000"/*蓝牙协议*/)); //GATT特性:用于构建 GATT 服务的基本数据元素 BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(UUID.fromString("00000000-0000-0000-0000-000000000000")); //设置数据,这里是我加密后的数据所以看起来有点不习惯 byte[] data = {-27, 36, 65, -70, -8, 94, 18, -12, -56, 116, -68, -124, 92, -19, 94, -91 }; //设置此特性的本地存储值。 characteristic.setValue(data); //写入到远程蓝牙设备 bluetoothGatt.writeCharacteristic(characteristic); }
Ble通知读取操作
//通知UUID BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(UUID.fromString("00000000-0000-0000-0000-000000000000")); enableNotification(bluetoothGatt,true,gattCharacteristic); public void enableNotification(BluetoothGatt bluetoothGatt, boolean enable, BluetoothGattCharacteristic characteristic) { if (bluetoothGatt == null || characteristic == null) { return; } if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) { return; } //获取到Notify当中的Descriptor通道 然后再进行注册 List<BluetoothGattDescriptor> clientConfigs = characteristic.getDescriptors(); if (clientConfigs == null || clientConfigs.size() ==0) { return; } if (enable) { for (BluetoothGattDescriptor clientConfig: clientConfigs) { clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(clientConfig); } } else { for (BluetoothGattDescriptor clientConfig: clientConfigs) { clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(clientConfig); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】