蓝牙作为一种短距离无线通信技术,在 Android 设备中应用广泛 —— 从蓝牙耳机、蓝牙音箱等音频设备,到蓝牙打印机、蓝牙传感器等数据传输设备,再到蓝牙手表等穿戴设备,都依赖蓝牙通讯实现交互。本文将从蓝牙技术基础出发,详解 Android 蓝牙通讯的两种核心模式(经典蓝牙、低功耗蓝牙)及开发实战,帮助开发者快速实现蓝牙设备连接与数据交互。
Android 支持两种主流蓝牙技术,适用场景差异显著:
蓝牙类型 |
全称 |
核心特点 |
适用场景 |
经典蓝牙(BR/EDR) |
Basic Rate/Enhanced Data Rate |
传输速率较高(1-3Mbps),通信距离约 10 米,功耗中等 |
音频传输(如蓝牙耳机)、大数据传输(如蓝牙打印机) |
低功耗蓝牙(BLE) |
Bluetooth Low Energy |
传输速率较低(约 1Mbps),通信距离 10-100 米,功耗极低 |
物联网设备(如温湿度传感器)、穿戴设备(如心率手环)、低频次数据传输(如门禁卡) |
关键区别:经典蓝牙适合 “高带宽、中功耗” 场景,BLE 适合 “低带宽、低功耗” 场景。本文将重点讲解两种模式的开发流程。
无论哪种蓝牙类型,通讯过程都涉及以下核心角色:
Android 蓝牙开发需在AndroidManifest.xml中声明权限,根据蓝牙类型和系统版本调整:
动态权限申请:Android 6.0+ 需在代码中动态申请危险权限(位置权限、蓝牙连接权限),否则扫描和连接会失败。
经典蓝牙适合 “一对一数据传输” 场景(如蓝牙串口通讯),开发流程为 “开启蓝牙→扫描设备→配对连接→数据传输→断开连接”。
经典蓝牙依赖android.bluetooth包下的类:
public class ClassicBluetoothHelper {
private Context mContext;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothSocket mSocket; // 连接成功后的socket
private 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() {
@Override
public 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);
扫描设备需通过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() {
@Override
public 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);
// 可将设备添加到列表展示
}
}
}
};
经典蓝牙连接需指定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()
);
// 关闭socket
try {
if (mSocket != null) {
mSocket.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
}
注意:经典蓝牙连接前通常需要 “配对”,部分设备可自动配对,部分需用户输入配对码(配对结果通过BluetoothDevice.ACTION_PAIRING_REQUEST广播通知)。
// 发送数据(字节数组)
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 {
@Override
public 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));
}
}
// 断开连接
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();
}
// 关闭socket
try {
if (mSocket != null) {
mSocket.close();
mSocket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
若需要 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 适合 “低功耗、频繁小数据传输” 场景(如传感器数据上报),与经典蓝牙的核心区别是基于 GATT 协议,通过 “服务(Service)” 和 “特征(Characteristic)” 定义数据结构。
BLE 依赖android.bluetooth.le包下的类,核心概念:
核心 API:
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;
}
}
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() {
@Override
public 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 records = Collections.singletonList(result.getScanRecord());
for (ScanRecord record : records) {
List uuids = record.getServiceUuids();
if (uuids != null && uuids.contains(ParcelUuid.fromString(TARGET_SERVICE_UUID))) {
// 发现目标设备,可停止扫描并连接
stopBLEScan();
connectDevice(device);
}
}
}
};
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() {
// 连接状态变化回调
@Override
public 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", "连接断开");
// 可尝试重连
}
}
// 发现服务回调(服务发现成功后调用)
@Override
public 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);
}
}
// 特征值变化回调(设备主动推送数据或读取数据成功时触发)
@Override
public 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 UUID
if (descriptor != null) {
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
: BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
return mGatt.writeDescriptor(descriptor);
}
return true;
}
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));
}
}
// 断开连接并释放资源
public void disconnect() {
if (mGatt != null) {
mGatt.disconnect();
mGatt.close(); // 必须调用close释放资源,否则下次连接可能失败
mGatt = null;
}
mDeviceAddress = null;
}
对比维度 |
经典蓝牙(BR/EDR) |
低功耗蓝牙(BLE) |
功耗 |
中(持续连接时较高) |
极低(适合电池供电设备) |
传输速率 |
1-3Mbps(适合大数据) |
约 1Mbps(适合小数据) |
通信距离 |
约 10 米 |
10-100 米(视环境而定) |
连接方式 |
基于 Socket,类似 TCP |
基于 GATT,服务 - 特征模型 |
开发复杂度 |
较低(类似网络 Socket) |
较高(需理解 GATT 协议) |
典型应用 |
蓝牙串口、蓝牙耳机、蓝牙打印机 |
传感器、穿戴设备、智能家居 |
选型建议:
Android 蓝牙通讯开发需根据场景选择技术类型:经典蓝牙适合高带宽数据传输,BLE 适合低功耗小数据交互。核心开发要点:
实际开发中,建议结合设备手册确认 UUID、数据格式和通讯协议(如传感器的数据包结构),并做好异常处理(重试机制、断连重连),以提升蓝牙通讯的稳定性。无论是经典蓝牙还是 BLE,掌握其核心 API 和协议逻辑后,都能快速实现设备间的无线数据交互。