一、BLE 客户端核心作用
客户端 = 扫描 → 连接 → 发现服务 → 读写特征 → 开启通知不需要广播,不需要创建 GattServer!
二、BLE 客户端 标准创建流程
1. 初始化蓝牙
获取BluetoothManager→BluetoothAdapter2. 开启蓝牙(如未开启)
3. 开启 BLE 扫描
通过BluetoothLeScanner.startScan()扫描周围设备4. 扫描到目标设备 → 停止扫描
5. 连接设备
device.connectGatt()→ 获取BluetoothGatt6. 发现服务
连接成功后调用gatt.discoverServices()7. 获取服务与特征值
根据 UUID 获取Service、Characteristic8. 读写数据 / 开启通知
读:gatt.readCharacteristic()
写:gatt.writeCharacteristic()
通知:设置特征值的 CCC 描述符
9. 回调处理
所有结果在BluetoothGattCallback中返回10. 断开连接 & 释放资源
gatt.disconnect()+gatt.close()
三、客户端 vs 服务端 最核心区别
四、 客户端完整代码
1. 权限(和服务端一样)
<uses-permissionandroid:name="android.permission.BLUETOOTH" /><uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" /><uses-permissionandroid:name="android.permission.BLUETOOTH_SCAN" /><uses-permissionandroid:name="android.permission.BLUETOOTH_CONNECT" /><uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION" /><uses-featureandroid:name="android.hardware.bluetooth_le"android:required="true" />
五、BleClientActivity.kt
import android.annotation.SuppressLintimport android.bluetooth.BluetoothAdapterimport android.bluetooth.BluetoothDeviceimport android.bluetooth.BluetoothGattimport android.bluetooth.BluetoothGattCallbackimport android.bluetooth.BluetoothGattCharacteristicimport android.bluetooth.BluetoothGattDescriptorimport android.bluetooth.BluetoothGattServiceimport android.bluetooth.BluetoothManagerimport android.bluetooth.le.BluetoothLeScannerimport android.bluetooth.le.ScanCallbackimport android.bluetooth.le.ScanResultimport android.content.Contextimport android.os.Bundleimport android.os.Handlerimport android.os.Looperimport android.widget.Toastimport androidx.appcompat.app.AppCompatActivityimport java.util.UUIDclass BleClientActivity : AppCompatActivity() { // ===================== 必须和服务端一致 ===================== private val SERVICE_UUID = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb") private val READ_CHAR_UUID = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb") private val WRITE_CHAR_UUID = UUID.fromString("0000ffe2-0000-1000-8000-00805f9b34fb") private val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") // ===================== 客户端核心对象 ===================== private var bluetoothManager: BluetoothManager? = null private var bluetoothAdapter: BluetoothAdapter? = null private var bleScanner: BluetoothLeScanner? = null private var bluetoothGatt: BluetoothGatt? = null override funonCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_ble_client) initBle() // 1. 初始化 startScan() // 2. 开始扫描 } // ===================== 1. 初始化蓝牙 ===================== private funinitBle() { bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager bluetoothAdapter = bluetoothManager?.adapter bleScanner = bluetoothAdapter?.bluetoothLeScanner if (bluetoothAdapter?.isEnabled == false) { bluetoothAdapter?.enable() } } // ===================== 2. 开始扫描 ===================== @SuppressLint("MissingPermission") private funstartScan() { showToast("开始扫描BLE设备...") bleScanner?.startScan(object : ScanCallback() { override funonScanResult(callbackType: Int, result: ScanResult) { super.onScanResult(callbackType, result) val device = result.device val name = device.name ?: "未知设备" // 找到目标设备 if (name.contains("你的设备名")) { showToast("找到设备:$name") bleScanner?.stopScan(this) // 停止扫描 connectGatt(device) // 连接设备 } } }) // 10秒后停止扫描 Handler(Looper.getMainLooper()).postDelayed({ bleScanner?.stopScan(object : ScanCallback() {}) }, 10000) } // ===================== 3. 连接GATT(客户端核心) ===================== @SuppressLint("MissingPermission") private funconnectGatt(device: BluetoothDevice) { bluetoothGatt = device.connectGatt(this, false, gattCallback) } // ===================== 4. GATT 回调(客户端核心) ===================== private val gattCallback = object : BluetoothGattCallback() { // 连接状态改变 override funonConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { super.onConnectionStateChange(gatt, status, newState) if (newState == BluetoothProfile.STATE_CONNECTED) { showToast("连接成功,开始发现服务") gatt?.discoverServices() // 发现服务 } } // 发现服务成功 override funonServicesDiscovered(gatt: BluetoothGatt?, status: Int) { super.onServicesDiscovered(gatt, status) showToast("发现服务成功") val service = gatt?.getService(SERVICE_UUID) val readChar = service?.getCharacteristic(READ_CHAR_UUID) val writeChar = service?.getCharacteristic(WRITE_CHAR_UUID) // 读取数据 gatt?.readCharacteristic(readChar) // 写入数据 writeChar?.value = "Hello BLE Server".toByteArray() val writeGatt=gatt?.writeCharacteristic(writeChar) // 开启通知 enableNotification(gatt, writeGatt) } // 读取到数据 override funonCharacteristicRead(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) { super.onCharacteristicRead(gatt, characteristic, status) val data = characteristic?.value?.toString() showToast("读取到:$data") } // 收到通知 override funonCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) { super.onCharacteristicChanged(gatt, characteristic) val msg = String(characteristic?.value ?: byteArrayOf()) showToast("收到通知:$msg") } } // ===================== 开启通知 ===================== @SuppressLint("MissingPermission") private funenableNotification(gatt: BluetoothGatt?, char: BluetoothGattCharacteristic?) { gatt?.setCharacteristicNotification(char, true) val descriptor = char?.getDescriptor(CCC_DESCRIPTOR_UUID) descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt?.writeDescriptor(descriptor) } // ===================== 释放资源 ===================== @SuppressLint("MissingPermission") override funonDestroy() { super.onDestroy() bluetoothGatt?.disconnect() bluetoothGatt?.close() } private funshowToast(msg: String) { runOnUiThread { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() } }}
六、客户端流程 超清晰总结
初始化蓝牙→ 获取适配器
扫描设备→startScan()
找到设备→ 停止扫描
连接 GATT→device.connectGatt()
发现服务→discoverServices()
获取特征→ 根据 UUID 获取
读写 / 通知→ 操作特征值
回调接收数据
断开并释放资源
七、客户端 最重要注意事项
客户端不需要广播
连接使用 device.connectGatt ()
必须在发现服务后才能读写
MTU 由客户端调用 requestMtu () 设置
开启通知必须写CCC 描述符(2902)
读写、发现服务都是异步操作
必须在最后close()释放 Gatt