0%

安卓蓝牙相关

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())
}
}