Android 蓝牙通讯全解析:从基础到实战
蓝牙作为一种短距离无线通信技术,在 Android 设备中应用广泛 —— 从蓝牙耳机、蓝牙音箱等音频设备,到蓝牙打印机、蓝牙传感器等数据传输设备,再到蓝牙手表等穿戴设备,都依赖蓝牙通讯实现交互。本文将从蓝牙技术基础出发,详解 Android 蓝牙通讯的两种核心模式(经典蓝牙、低功耗蓝牙)及开发实战,帮助开发者快速实现蓝牙设备连接与数据交互。
一、Android 蓝牙技术基础
1.1 蓝牙技术分类与应用场景
Android 支持两种主流蓝牙技术,适用场景差异显著:
蓝牙类型 | 全称 | 核心特点 | 适用场景 |
经典蓝牙(BR/EDR) | Basic Rate/Enhanced Data Rate | 传输速率较高(1-3Mbps),通信距离约 10 米,功耗中等 | 音频传输(如蓝牙耳机)、大数据传输(如蓝牙打印机) |
低功耗蓝牙(BLE) | Bluetooth Low Energy | 传输速率较低(约 1Mbps),通信距离 10-100 米,功耗极低 | 物联网设备(如温湿度传感器)、穿戴设备(如心率手环)、低频次数据传输(如门禁卡) |
关键区别:经典蓝牙适合 “高带宽、中功耗” 场景,BLE 适合 “低带宽、低功耗” 场景。本文将重点讲解两种模式的开发流程。
1.2 蓝牙通讯核心概念
无论哪种蓝牙类型,通讯过程都涉及以下核心角色:
- 蓝牙适配器(BluetoothAdapter):Android 设备的蓝牙硬件接口,负责开启 / 关闭蓝牙、扫描设备、管理连接。
- 蓝牙设备(BluetoothDevice):远程蓝牙设备的抽象,包含设备地址(MAC 地址)、名称、类型等信息。
- UUID(通用唯一识别码):用于标识蓝牙服务或数据通道(如串口服务 UUID 为00001101-0000-1000-8000-00805F9B34FB)。
- GATT(通用属性配置文件):BLE 通讯的核心协议,通过 “服务(Service)- 特征(Characteristic)” 结构定义数据交互格式(经典蓝牙无需 GATT)。
1.3 蓝牙权限配置
Android 蓝牙开发需在AndroidManifest.xml中声明权限,根据蓝牙类型和系统版本调整:
<!-- 基础蓝牙权限(所有蓝牙功能必需) -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><!-- Android 6.0+ 位置权限(蓝牙扫描需获取设备位置,因早期蓝牙与位置服务关联) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><!-- Android 12+ 新增蓝牙权限(替代旧权限) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <!-- 扫描权限,禁用位置关联 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <!-- 广播权限(BLE作为外设时需用) -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 连接权限 --><!-- 声明设备支持蓝牙(可选,用于Google Play过滤) -->
<uses-featureandroid:name="android.hardware.bluetooth"android:required="true" />
<uses-featureandroid:name="android.hardware.bluetooth_le"android:required="false" /> <!-- BLE可选支持 -->
动态权限申请:Android 6.0+ 需在代码中动态申请危险权限(位置权限、蓝牙连接权限),否则扫描和连接会失败。
二、经典蓝牙(BR/EDR)开发实战
经典蓝牙适合 “一对一数据传输” 场景(如蓝牙串口通讯),开发流程为 “开启蓝牙→扫描设备→配对连接→数据传输→断开连接”。
2.1 核心 API 与工具类
经典蓝牙依赖android.bluetooth包下的类:
- BluetoothAdapter:蓝牙适配器,核心入口;
- BluetoothDevice:远程设备,通过 MAC 地址或名称标识;
- BluetoothSocket:数据传输的 socket(类似 TCP socket);
- BluetoothServerSocket:作为服务端时监听连接的 socket。
2.2 开发流程与代码实现
步骤 1:初始化蓝牙适配器并开启蓝牙
public class ClassicBluetoothHelper {private Context mContext;private BluetoothAdapter mBluetoothAdapter;private BluetoothSocket mSocket; // 连接成功后的socketprivate OutputStream mOutputStream; // 发送数据private InputStream mInputStream; // 接收数据private ReadThread mReadThread; // 接收线程public ClassicBluetoothHelper(Context context) {mContext = context;// 获取蓝牙适配器(设备唯一的蓝牙入口)mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();}// 检查并开启蓝牙public boolean enableBluetooth() {if (mBluetoothAdapter == null) {// 设备不支持蓝牙Toast.makeText(mContext, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();return false;}// 若蓝牙未开启,请求开启if (!mBluetoothAdapter.isEnabled()) {Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);mContext.startActivity(enableIntent);return false; // 需等待用户授权,授权后通过广播接收结果}return true;}
}
监听蓝牙状态变化:注册广播接收者,监听蓝牙开启 / 关闭状态:
// 在Activity中注册广播
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);if (state == BluetoothAdapter.STATE_ON) {Toast.makeText(context, "蓝牙已开启", Toast.LENGTH_SHORT).show();// 蓝牙开启后可开始扫描设备startScan();}}}
};// 注册广播
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothReceiver, filter);
步骤 2:扫描并发现设备
扫描设备需通过BluetoothAdapter.startDiscovery(),并注册广播接收扫描结果:
// 开始扫描设备
public void startScan() {// 停止正在进行的扫描(避免重复扫描)if (mBluetoothAdapter.isDiscovering()) {mBluetoothAdapter.cancelDiscovery();}// 注册扫描结果广播IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);mContext.registerReceiver(mDeviceReceiver, filter);// 开始扫描(耗时操作,约12秒,需在子线程或异步处理)mBluetoothAdapter.startDiscovery();
}// 扫描结果广播接收者
private BroadcastReceiver mDeviceReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (BluetoothDevice.ACTION_FOUND.equals(action)) {// 从intent中获取发现的设备BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (device != null) {// 设备名称(可能为null)和MAC地址String name = device.getName() == null ? "未知设备" : device.getName();String address = device.getAddress();Log.d("ClassicBT", "发现设备:" + name + ",MAC:" + address);// 可将设备添加到列表展示}}}
};
步骤 3:连接设备(客户端模式)
经典蓝牙连接需指定UUID(常用串口 UUID:00001101-0000-1000-8000-00805F9B34FB),连接过程需在子线程中进行(避免阻塞主线程):
// 连接指定设备(客户端模式)
public void connectDevice(BluetoothDevice device) {new Thread(() -> {try {// 取消扫描(扫描会占用蓝牙资源,影响连接)mBluetoothAdapter.cancelDiscovery();// 通过UUID创建socket(使用串口服务UUID)UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");mSocket = device.createRfcommSocketToServiceRecord(uuid);// 建立连接(阻塞操作,需在子线程)mSocket.connect();// 连接成功,获取读写流mOutputStream = mSocket.getOutputStream();mInputStream = mSocket.getInputStream();// 启动接收线程mReadThread = new ReadThread();mReadThread.start();// 通知UI连接成功((Activity) mContext).runOnUiThread(() -> Toast.makeText(mContext, "连接成功", Toast.LENGTH_SHORT).show());} catch (IOException e) {// 连接失败(常见原因:未配对、UUID错误、设备不支持串口服务)e.printStackTrace();((Activity) mContext).runOnUiThread(() -> Toast.makeText(mContext, "连接失败:" + e.getMessage(), Toast.LENGTH_SHORT).show());// 关闭sockettry {if (mSocket != null) {mSocket.close();}} catch (IOException ex) {ex.printStackTrace();}}}).start();
}
注意:经典蓝牙连接前通常需要 “配对”,部分设备可自动配对,部分需用户输入配对码(配对结果通过BluetoothDevice.ACTION_PAIRING_REQUEST广播通知)。
步骤 4:数据传输(发送与接收)
// 发送数据(字节数组)
public void sendData(byte[] data) {if (mOutputStream == null) {Toast.makeText(mContext, "未连接设备", Toast.LENGTH_SHORT).show();return;}try {mOutputStream.write(data);mOutputStream.flush();Log.d("ClassicBT", "发送数据:" + new String(data));} catch (IOException e) {e.printStackTrace();Toast.makeText(mContext, "发送失败", Toast.LENGTH_SHORT).show();}
}// 接收数据的线程(必须在子线程,避免阻塞)
private class ReadThread extends Thread {@Overridepublic void run() {byte[] buffer = new byte[1024];int bytes;while (true) {try {// 读取数据(阻塞操作,无数据时会等待)bytes = mInputStream.read(buffer);if (bytes > 0) {byte[] received = new byte[bytes];System.arraycopy(buffer, 0, received, 0, bytes);// 转换为字符串(根据实际协议解析)String data = new String(received, StandardCharsets.UTF_8);Log.d("ClassicBT", "收到数据:" + data);// 通知UI(通过接口回调或Handler)notifyDataReceived(data);}} catch (IOException e) {// 读取失败(通常是连接断开)e.printStackTrace();break;}}}
}// 数据接收回调(供UI层处理)
private OnDataReceivedListener mListener;
public interface OnDataReceivedListener {void onReceived(String data);
}
public void setOnDataReceivedListener(OnDataReceivedListener listener) {mListener = listener;
}
private void notifyDataReceived(String data) {if (mListener != null) {((Activity) mContext).runOnUiThread(() -> mListener.onReceived(data));}
}
步骤 5:断开连接与资源释放
// 断开连接
public void disconnect() {// 停止接收线程if (mReadThread != null) {mReadThread.interrupt();mReadThread = null;}// 关闭流try {if (mInputStream != null) {mInputStream.close();}if (mOutputStream != null) {mOutputStream.close();}} catch (IOException e) {e.printStackTrace();}// 关闭sockettry {if (mSocket != null) {mSocket.close();mSocket = null;}} catch (IOException e) {e.printStackTrace();}
}
2.3 服务端模式(可选)
若需要 Android 设备作为 “蓝牙服务端”(如接收其他设备连接),需使用BluetoothServerSocket监听连接:
// 服务端监听连接(在子线程中执行)
private void startServer() {new Thread(() -> {BluetoothServerSocket serverSocket = null;try {// 创建服务器socket(UUID需与客户端一致)UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");serverSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("ClassicBT_Server", uuid); // 名称用于客户端识别// 阻塞等待客户端连接(只处理一次连接,如需多连接需循环)mSocket = serverSocket.accept();// 连接成功后,关闭serverSocket(单连接模式)serverSocket.close();// 后续流程:获取流、启动接收线程(同客户端)mOutputStream = mSocket.getOutputStream();mInputStream = mSocket.getInputStream();mReadThread = new ReadThread();mReadThread.start();} catch (IOException e) {e.printStackTrace();} finally {try {if (serverSocket != null) {serverSocket.close();}} catch (IOException e) {e.printStackTrace();}}}).start();
}
三、低功耗蓝牙(BLE)开发实战
BLE 适合 “低功耗、频繁小数据传输” 场景(如传感器数据上报),与经典蓝牙的核心区别是基于 GATT 协议,通过 “服务(Service)” 和 “特征(Characteristic)” 定义数据结构。
3.1 BLE 核心概念与 API
BLE 依赖android.bluetooth.le包下的类,核心概念:
- GATT:通用属性配置文件,定义了设备间数据交互的规则;
- Service:服务,包含多个特征(如 “心率服务” 包含 “心率测量特征”);
- Characteristic:特征,数据的最小单元(可读写、通知);
- UUID:服务和特征的唯一标识(如心率测量特征 UUID:00002a37-0000-1000-8000-00805f9b34fb);
- Advertising:BLE 设备广播自身信息(如名称、服务 UUID),供其他设备扫描。
核心 API:
- BluetoothLeScanner:BLE 扫描工具(替代经典蓝牙的BluetoothAdapter扫描);
- BluetoothGatt:GATT 客户端,负责连接和数据交互;
- BluetoothGattCallback:GATT 操作的回调(连接状态、数据接收等)。
3.2 开发流程与代码实现
步骤 1:初始化 BLE 适配器与权限
public class BLEHelper {private Context mContext;private BluetoothAdapter mBluetoothAdapter;private BluetoothLeScanner mLeScanner; // BLE扫描器private BluetoothGatt mGatt; // GATT客户端private String mDeviceAddress; // 连接的设备MAC地址public BLEHelper(Context context) {mContext = context;mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if (mBluetoothAdapter != null) {mLeScanner = mBluetoothAdapter.getBluetoothLeScanner();}}// 检查BLE支持与权限(需动态申请BLUETOOTH_SCAN等权限)public boolean checkBLESupport() {if (mBluetoothAdapter == null) {Toast.makeText(mContext, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();return false;}if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {Toast.makeText(mContext, "设备不支持BLE", Toast.LENGTH_SHORT).show();return false;}return true;}
}
步骤 2:扫描 BLE 设备
BLE 扫描需使用BluetoothLeScanner,支持设置过滤条件(如只扫描指定服务 UUID 的设备):
// 开始扫描BLE设备(需BLUETOOTH_SCAN权限)
public void startBLEScan() {if (mLeScanner == null) {return;}// 扫描设置(可选:设置扫描周期、过滤条件)ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 低延迟模式(功耗较高).build();// 开始扫描(回调在主线程,扫描结果通过ScanCallback返回)mLeScanner.startScan(null, settings, mScanCallback);// 扫描一段时间后自动停止(避免功耗过高)new Handler(Looper.getMainLooper()).postDelayed(this::stopBLEScan, 10000); // 10秒后停止
}// 停止扫描
public void stopBLEScan() {if (mLeScanner != null) {mLeScanner.stopScan(mScanCallback);}
}// 扫描回调
private ScanCallback mScanCallback = new ScanCallback() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {super.onScanResult(callbackType, result);BluetoothDevice device = result.getDevice();if (device == null) return;// 获取设备信息String name = device.getName() == null ? "未知设备" : device.getName();String address = device.getAddress();int rssi = result.getRssi(); // 信号强度(负值,越接近0信号越强)Log.d("BLE", "发现设备:" + name + ",MAC:" + address + ",信号:" + rssi);// 获取设备广播的服务UUID(判断是否是目标设备)List<ScanRecord> records = Collections.singletonList(result.getScanRecord());for (ScanRecord record : records) {List<ParcelUuid> uuids = record.getServiceUuids();if (uuids != null && uuids.contains(ParcelUuid.fromString(TARGET_SERVICE_UUID))) {// 发现目标设备,可停止扫描并连接stopBLEScan();connectDevice(device);}}}
};
步骤 3:连接设备并发现服务
BLE 连接通过BluetoothGatt实现,连接后需 “发现服务”(获取设备支持的 Service 和 Characteristic):
// 连接BLE设备(需BLUETOOTH_CONNECT权限)
public void connectDevice(BluetoothDevice device) {if (device == null) return;mDeviceAddress = device.getAddress();// 连接设备(autoConnect设为false表示立即连接,true表示当设备可用时自动连接)mGatt = device.connectGatt(mContext, false, mGattCallback);
}// GATT回调(所有BLE操作的结果都通过此回调返回)
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {// 连接状态变化回调@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {if (newState == BluetoothProfile.STATE_CONNECTED) {// 连接成功,开始发现服务(必须在连接成功后调用)Log.d("BLE", "连接成功,开始发现服务");gatt.discoverServices();} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {// 连接断开Log.d("BLE", "连接断开");// 可尝试重连}}// 发现服务回调(服务发现成功后调用)@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {// 服务发现成功,获取目标服务和特征BluetoothGattService service = gatt.getService(UUID.fromString(TARGET_SERVICE_UUID));if (service != null) {// 获取可读写的特征(根据设备协议确定特征UUID)BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(TARGET_CHARACTERISTIC_UUID));if (characteristic != null) {// 启用特征通知(当特征值变化时,设备主动推送数据)setCharacteristicNotification(characteristic, true);}}} else {Log.e("BLE", "服务发现失败,状态码:" + status);}}// 特征值变化回调(设备主动推送数据或读取数据成功时触发)@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);// 读取特征值(设备推送的数据)byte[] data = characteristic.getValue();if (data != null) {String value = new String(data, StandardCharsets.UTF_8);Log.d("BLE", "收到数据:" + value);// 通知UI层处理notifyDataReceived(value);}}
};// 启用特征通知(让设备在数据变化时主动推送)
public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) {if (mGatt == null || characteristic == null) return false;// 1. 启用本地通知if (!mGatt.setCharacteristicNotification(characteristic, enable)) {return false;}// 2. 写入CCCD描述符(告知设备允许通知,部分设备需要)BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); // 标准CCCD UUIDif (descriptor != null) {descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);return mGatt.writeDescriptor(descriptor);}return true;
}
步骤 4:数据传输(读写特征值)
BLE 数据传输通过 “读写特征值” 实现:向 Characteristic 写入数据(发送),读取 Characteristic 的值(接收),或通过通知接收设备主动推送的数据。
// 向特征写入数据(发送数据)
public boolean writeData(String characteristicUuid, byte[] data) {if (mGatt == null) return false;// 获取目标特征BluetoothGattService service = mGatt.getService(UUID.fromString(TARGET_SERVICE_UUID));if (service == null) return false;BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicUuid));if (characteristic == null) return false;// 设置写入类型(根据设备支持的类型选择)characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);characteristic.setValue(data);// 写入数据(结果通过onCharacteristicWrite回调返回)return mGatt.writeCharacteristic(characteristic);
}// 读取特征值(主动获取数据)
public boolean readData(String characteristicUuid) {if (mGatt == null) return false;BluetoothGattService service = mGatt.getService(UUID.fromString(TARGET_SERVICE_UUID));if (service == null) return false;BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicUuid));if (characteristic == null) return false;// 读取数据(结果通过onCharacteristicRead回调返回)return mGatt.readCharacteristic(characteristic);
}// 在GATT回调中处理读写结果
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {Log.d("BLE", "数据写入成功");} else {Log.e("BLE", "数据写入失败");}
}@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {byte[] data = characteristic.getValue();Log.d("BLE", "读取数据:" + new String(data, StandardCharsets.UTF_8));}
}
步骤 5:断开连接与释放资源
// 断开连接并释放资源
public void disconnect() {if (mGatt != null) {mGatt.disconnect();mGatt.close(); // 必须调用close释放资源,否则下次连接可能失败mGatt = null;}mDeviceAddress = null;
}
四、经典蓝牙与 BLE 的对比及选型建议
对比维度 | 经典蓝牙(BR/EDR) | 低功耗蓝牙(BLE) |
功耗 | 中(持续连接时较高) | 极低(适合电池供电设备) |
传输速率 | 1-3Mbps(适合大数据) | 约 1Mbps(适合小数据) |
通信距离 | 约 10 米 | 10-100 米(视环境而定) |
连接方式 | 基于 Socket,类似 TCP | 基于 GATT,服务 - 特征模型 |
开发复杂度 | 较低(类似网络 Socket) | 较高(需理解 GATT 协议) |
典型应用 | 蓝牙串口、蓝牙耳机、蓝牙打印机 | 传感器、穿戴设备、智能家居 |
选型建议:
- 传输音频或大数据(如图片、文件)→ 经典蓝牙;
- 低功耗需求(如电池供电的传感器)→ BLE;
- 需频繁小数据交互(如温湿度上报)→ BLE;
- 设备需兼容旧蓝牙协议(如传统蓝牙串口设备)→ 经典蓝牙。
五、常见问题及解决方案
5.1 扫描不到设备
- 原因:未开启蓝牙、缺少权限(位置权限、蓝牙扫描权限)、设备未处于可发现模式、设备距离过远。
- 解决:
- 确认蓝牙已开启,权限已授予(尤其是 Android 12 + 的 BLUETOOTH_SCAN 权限);
- 经典蓝牙需确保设备处于 “可发现模式”(在蓝牙设置中勾选 “允许被发现”);
- BLE 设备需正在广播(部分设备休眠时停止广播);
- 缩短设备距离,避开遮挡物。
5.2 连接失败(经典蓝牙)
- 原因:设备未配对、UUID 不匹配、设备不支持目标服务(如串口服务)、蓝牙被占用。
- 解决:
- 手动配对设备(在系统蓝牙设置中完成配对);
- 确认使用正确的 UUID(串口设备常用00001101-...);
- 关闭其他占用蓝牙的应用(如音乐播放器);
- 检查设备是否支持 RFCOMM 协议(经典蓝牙数据传输依赖此协议)。
5.3 BLE 连接后无法发现服务
- 原因:连接未稳定就调用discoverServices、设备不支持 GATT 服务、蓝牙信号弱导致连接中断。
- 解决:
- 确保discoverServices在onConnectionStateChange回调中连接成功后调用;
- 重试连接(BLE 连接稳定性较差,可多次尝试);
- 靠近设备,确保信号强度(RSSI > -80dBm)。
5.4 数据传输不完整或丢失
- 原因:数据长度超过 MTU(最大传输单元)、未启用通知、写入类型不匹配。
- 解决:
- BLE 需协商 MTU(调用requestMtu设置更大的 MTU,如 512 字节);
- 确认启用特征通知(setCharacteristicNotification);
- 经典蓝牙确保OutputStream正确flush,BLE 使用正确的写入类型(如WRITE_TYPE_NO_RESPONSE适合批量数据)。
六、总结
Android 蓝牙通讯开发需根据场景选择技术类型:经典蓝牙适合高带宽数据传输,BLE 适合低功耗小数据交互。核心开发要点:
- 权限处理:动态申请位置权限和蓝牙权限,尤其是 Android 12 + 的新增权限;
- 线程管理:扫描、连接、数据读写等耗时操作必须在子线程执行,避免 ANR;
- 回调处理:所有蓝牙操作结果通过广播或回调返回,需正确处理连接状态和数据交互;
- 资源释放:断开连接后必须关闭 socket 或 Gatt,否则会导致资源泄漏和下次连接失败。
实际开发中,建议结合设备手册确认 UUID、数据格式和通讯协议(如传感器的数据包结构),并做好异常处理(重试机制、断连重连),以提升蓝牙通讯的稳定性。无论是经典蓝牙还是 BLE,掌握其核心 API 和协议逻辑后,都能快速实现设备间的无线数据交互。