1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
| 安卓蓝牙分为: 经典蓝牙和低功耗蓝牙 经典蓝牙 BR/EDR (Bluetooth Basic Rate / Enhanced Data Rate) 面向流,数据吞吐高(~2~3 Mbps),功耗高 音频、打印机、串口设备、手柄、POS机 低功耗蓝牙 BLE (Bluetooth Low Energy / Bluetooth Smart) 面向属性(GATT),数据吞吐低(单次有效载荷 ≤ 20B 默认),功耗低 健康设备、IoT 传感器、可穿戴设
经典蓝牙
适合大数据流、串口通信。
需要配对才能连接。
读写阻塞,需独立线程或协程。
UUID 必须正确,否则 connect() 会失败。
异常:read failed, socket might closed or timeout 常因 UUID 错误、设备拒绝或被系统占用。
BLE
适合短小数据,低功耗应用。
扫描、连接、读写全异步。
单次数据量有限,需要分包处理大数据。
可以设置 MTU(requestMtu())增加单次传输量。
特征值通知(Notify/Indicate)是常用的数据推送方式。
不一定需要配对,但安全通信可使用加密。
蓝牙连接状态安卓系统机制没有给出,需自己获取判断: 1.通过反射方式调用隐藏 API(稳定但非官方公开):
fun isDeviceConnected(device: BluetoothDevice): Boolean { return try { val method = device.javaClass.getMethod("isConnected") method.invoke(device) as Boolean } catch (e: Exception) { false } }
✅ 这个方法在 Android 6.0+ 都可用,实测能判断系统层面蓝牙是否连接成功(包括从“设置”界面连接的情况)。
2.监听系统广播(推荐结合使用) val filter = IntentFilter().apply { addAction(BluetoothDevice.ACTION_ACL_CONNECTED) addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED) } context.registerReceiver(receiver, filter)
配合反射 isConnected() 使用最准确。
import android.annotation.SuppressLint import android.bluetooth.* import android.content.* import android.os.Handler import android.os.Looper import android.util.Log import androidx.lifecycle.MutableLiveData import java.io.IOException import java.util.*
@SuppressLint("MissingPermission") object BluetoothUtil {
private const val TAG = "BluetoothUtil" private val SPP_UUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
interface Callback { fun onScanStarted() {} fun onDeviceFound(device: BluetoothDevice) {} fun onScanFinished() {} fun onBondStateChanged(device: BluetoothDevice, state: Int) {} fun onConnected(device: BluetoothDevice) {} fun onDisconnected(device: BluetoothDevice) {} fun onMessageReceived(device: BluetoothDevice, data: ByteArray) {} fun onError(message: String) {} }
// ------------------------- 内部状态 ------------------------- private var callback: Callback? = null private var adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() private var socket: BluetoothSocket? = null private var currentDevice: BluetoothDevice? = null private var connectThread: Thread? = null private var readThread: Thread? = null private var isConnecting = false private var isConnected = false private var stopReading = false private lateinit var appContext: Context private val handler = Handler(Looper.getMainLooper())
// 已连接设备状态 val connectedDevicesLiveData = MutableLiveData<MutableSet<BluetoothDevice>>(mutableSetOf())
// ------------------------- 扫描 ------------------------- private var discoveryReceiver: BroadcastReceiver? = null
fun init(context: Context, cb: Callback) { appContext = context.applicationContext callback = cb adapter = BluetoothAdapter.getDefaultAdapter() registerSystemConnectionReceiver() }
fun startDiscovery() { if (adapter == null || !adapter!!.isEnabled) { callback?.onError("蓝牙未开启") return } registerDiscoveryReceiver() adapter?.bondedDevices?.forEach { callback?.onDeviceFound(it) } adapter?.startDiscovery() callback?.onScanStarted() }
fun stopDiscovery() { adapter?.takeIf { it.isDiscovering }?.cancelDiscovery() discoveryReceiver?.let { try { appContext.unregisterReceiver(it) } catch (_: Exception) {} discoveryReceiver = null } callback?.onScanFinished() }
private fun registerDiscoveryReceiver() { if (discoveryReceiver != null) return discoveryReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when(intent.action){ BluetoothDevice.ACTION_FOUND -> { intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) ?.let { callback?.onDeviceFound(it) } } BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> { callback?.onScanFinished() try { appContext.unregisterReceiver(this) } catch (_: Exception){} discoveryReceiver = null } BluetoothDevice.ACTION_BOND_STATE_CHANGED -> { val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,-1) device?.let { callback?.onBondStateChanged(it, state) } } } } } val filter = IntentFilter().apply { addAction(BluetoothDevice.ACTION_FOUND) addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED) addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED) } appContext.registerReceiver(discoveryReceiver, filter) }
// ------------------------- 配对 ------------------------- fun bondDevice(device: BluetoothDevice) { try { if (device.bondState == BluetoothDevice.BOND_NONE) { device.javaClass.getMethod("createBond").invoke(device) } else { callback?.onBondStateChanged(device, device.bondState) } } catch (e: Exception) { callback?.onError("配对失败: ${e.message}") } }
fun removeBond(device: BluetoothDevice) { try { device.javaClass.getMethod("removeBond").invoke(device) } catch (e: Exception) { callback?.onError("取消配对失败: ${e.message}") } }
// ------------------------- 连接 ------------------------- fun connect(device: BluetoothDevice) { if (isConnecting || isConnected) return isConnecting = true currentDevice = device stopDiscovery()
connectThread = Thread { try { val uuid = device.uuids?.firstOrNull()?.uuid ?: SPP_UUID socket = device.createRfcommSocketToServiceRecord(uuid) socket?.connect()
isConnected = true isConnecting = false addConnectedDevice(device) callback?.onConnected(device) startReading() } catch (e: IOException) { isConnecting = false isConnected = false callback?.onError("连接失败: ${e.message}") disconnect() } }.apply { start() } }
fun disconnect() { stopReading = true isConnected = false try { socket?.close() } catch (_: IOException) {} socket = null currentDevice?.let { removeConnectedDevice(it) callback?.onDisconnected(it) } }
private fun startReading() { stopReading = false readThread = Thread { try { val buffer = ByteArray(1024) val input = socket?.inputStream while (!stopReading && input != null) { val len = input.read(buffer) if (len != -1) { val data = buffer.copyOf(len) currentDevice?.let { callback?.onMessageReceived(it, data) } } } } catch (e: IOException) { if (!stopReading) { callback?.onError("读取异常: ${e.message}") disconnect() } } }.apply { start() } }
fun sendMessage(msg: String) = sendBytes(msg.toByteArray()) fun sendBytes(data: ByteArray) { if (!isConnected || socket == null) { callback?.onError("未连接设备") return } try { socket?.outputStream?.apply { write(data) flush() } } catch (e: IOException) { callback?.onError("发送失败: ${e.message}") } }
// ------------------------- 系统蓝牙状态 ------------------------- private var systemReceiver: BroadcastReceiver? = null
private fun registerSystemConnectionReceiver() { if (systemReceiver != null) return systemReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val action = intent?.action ?: return val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) ?: return when(action){ BluetoothDevice.ACTION_ACL_CONNECTED -> { Log.d(TAG, "系统连接: ${device.name}") addConnectedDevice(device) callback?.onConnected(device) } BluetoothDevice.ACTION_ACL_DISCONNECTED -> { Log.d(TAG, "系统断开: ${device.name}") removeConnectedDevice(device) callback?.onDisconnected(device) } } } } val filter = IntentFilter().apply { addAction(BluetoothDevice.ACTION_ACL_CONNECTED) addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED) } appContext.registerReceiver(systemReceiver, filter) }
private fun addConnectedDevice(device: BluetoothDevice) { val set = connectedDevicesLiveData.value ?: mutableSetOf() set.add(device) connectedDevicesLiveData.postValue(set) }
private fun removeConnectedDevice(device: BluetoothDevice) { val set = connectedDevicesLiveData.value ?: mutableSetOf() set.remove(device) connectedDevicesLiveData.postValue(set) }
// ------------------------- 工具 ------------------------- fun isBluetoothEnabled(): Boolean = adapter?.isEnabled == true fun enableBluetooth() { adapter?.enable() } fun disableBluetooth() { adapter?.disable() }
fun release() { stopDiscovery() disconnect() currentDevice = null socket = null systemReceiver?.let { try { appContext.unregisterReceiver(it) } catch (_: Exception){} } systemReceiver = null connectedDevicesLiveData.postValue(mutableSetOf()) } }
|