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-feature
    android:name="android.hardware.bluetooth"
    android:required="true" />
<uses-feature
    android: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:初始化蓝牙适配器并开启蓝牙
java 复制代码
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;
    }
}

监听蓝牙状态变化:注册广播接收者,监听蓝牙开启 / 关闭状态:

java 复制代码
// 在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);
步骤 2:扫描并发现设备

扫描设备需通过BluetoothAdapter.startDiscovery(),并注册广播接收扫描结果:

java 复制代码
// 开始扫描设备
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);
                // 可将设备添加到列表展示
            }
        }
    }
};
步骤 3:连接设备(客户端模式)

经典蓝牙连接需指定UUID(常用串口 UUID:00001101-0000-1000-8000-00805F9B34FB),连接过程需在子线程中进行(避免阻塞主线程):

java 复制代码
// 连接指定设备(客户端模式)
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广播通知)。

步骤 4:数据传输(发送与接收)
java 复制代码
// 发送数据(字节数组)
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));
    }
}
步骤 5:断开连接与资源释放
java 复制代码
// 断开连接
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();
    }
}

2.3 服务端模式(可选)

若需要 Android 设备作为 "蓝牙服务端"(如接收其他设备连接),需使用BluetoothServerSocket监听连接:

java 复制代码
// 服务端监听连接(在子线程中执行)
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 适配器与权限
java 复制代码
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 的设备):

java 复制代码
// 开始扫描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<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):

java 复制代码
// 连接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;
}
步骤 4:数据传输(读写特征值)

BLE 数据传输通过 "读写特征值" 实现:向 Characteristic 写入数据(发送),读取 Characteristic 的值(接收),或通过通知接收设备主动推送的数据。

java 复制代码
// 向特征写入数据(发送数据)
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:断开连接与释放资源
java 复制代码
// 断开连接并释放资源
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 扫描不到设备

  • 原因:未开启蓝牙、缺少权限(位置权限、蓝牙扫描权限)、设备未处于可发现模式、设备距离过远。
  • 解决
  1. 确认蓝牙已开启,权限已授予(尤其是 Android 12 + 的 BLUETOOTH_SCAN 权限);
  2. 经典蓝牙需确保设备处于 "可发现模式"(在蓝牙设置中勾选 "允许被发现");
  3. BLE 设备需正在广播(部分设备休眠时停止广播);
  4. 缩短设备距离,避开遮挡物。

5.2 连接失败(经典蓝牙)

  • 原因:设备未配对、UUID 不匹配、设备不支持目标服务(如串口服务)、蓝牙被占用。
  • 解决
  1. 手动配对设备(在系统蓝牙设置中完成配对);
  2. 确认使用正确的 UUID(串口设备常用00001101-...);
  3. 关闭其他占用蓝牙的应用(如音乐播放器);
  4. 检查设备是否支持 RFCOMM 协议(经典蓝牙数据传输依赖此协议)。

5.3 BLE 连接后无法发现服务

  • 原因:连接未稳定就调用discoverServices、设备不支持 GATT 服务、蓝牙信号弱导致连接中断。
  • 解决
  1. 确保discoverServices在onConnectionStateChange回调中连接成功后调用;
  2. 重试连接(BLE 连接稳定性较差,可多次尝试);
  3. 靠近设备,确保信号强度(RSSI > -80dBm)。

5.4 数据传输不完整或丢失

  • 原因:数据长度超过 MTU(最大传输单元)、未启用通知、写入类型不匹配。
  • 解决
  1. BLE 需协商 MTU(调用requestMtu设置更大的 MTU,如 512 字节);
  2. 确认启用特征通知(setCharacteristicNotification);
  3. 经典蓝牙确保OutputStream正确flush,BLE 使用正确的写入类型(如WRITE_TYPE_NO_RESPONSE适合批量数据)。

六、总结

Android 蓝牙通讯开发需根据场景选择技术类型:经典蓝牙适合高带宽数据传输,BLE 适合低功耗小数据交互。核心开发要点:

  • 权限处理:动态申请位置权限和蓝牙权限,尤其是 Android 12 + 的新增权限;
  • 线程管理:扫描、连接、数据读写等耗时操作必须在子线程执行,避免 ANR;
  • 回调处理:所有蓝牙操作结果通过广播或回调返回,需正确处理连接状态和数据交互;
  • 资源释放:断开连接后必须关闭 socket 或 Gatt,否则会导致资源泄漏和下次连接失败。

实际开发中,建议结合设备手册确认 UUID、数据格式和通讯协议(如传感器的数据包结构),并做好异常处理(重试机制、断连重连),以提升蓝牙通讯的稳定性。无论是经典蓝牙还是 BLE,掌握其核心 API 和协议逻辑后,都能快速实现设备间的无线数据交互。

相关推荐
HAPPY酷20 小时前
构造与析构:C++ 中对象的温柔生灭
java·jvm·c++
darkb1rd20 小时前
五、PHP类型转换与类型安全
android·安全·php
lang2015092820 小时前
Java JSR 250核心注解全解析
java·开发语言
czhc114007566320 小时前
协议 25
java·开发语言·算法
逆光的July20 小时前
如何解决超卖问题
java
落花流水 丶20 小时前
Java 集合框架完全指南
java
gjxDaniel20 小时前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
csj5020 小时前
安卓基础之《(22)—高级控件(4)碎片Fragment》
android
Sharewinfo_BJ20 小时前
PowerBI 2026年1月功能更新|效率升级,体验再优化
windows·microsoft·powerbi
lang2015092821 小时前
Java WebSocket API:JSR-356详解
java·python·websocket