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 和协议逻辑后,都能快速实现设备间的无线数据交互。

相关推荐
小刘|2 小时前
Spring 核心知识点梳理 1
java·后端·spring
小张快跑。2 小时前
【Java企业级开发】(六)Java框架技术-Maven和MyBatis
java·开发语言·maven·mybatis
光の3 小时前
创意 C++ 文本冒险战斗游戏代码
microsoft
吃饭只吃七分饱4 小时前
C++ primer知识点总结
java·开发语言·c++
半部论语4 小时前
Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」
java·spring boot
2301_803554525 小时前
C++中的detach
java·开发语言·jvm
失去的青春---夕阳下的奔跑5 小时前
安卓第一个项目
android·安卓·摄像头
源码_V_saaskw5 小时前
JAVA国际版任务悬赏+接单系统源码支持IOS+Android+H5
android·java·开发语言·javascript·微信小程序
双力臂4046 小时前
Java IO流体系详解:字节流、字符流与NIO/BIO对比及文件拷贝实践
java·开发语言·nio
钮钴禄·爱因斯晨7 小时前
Java API (二):从 Object 类到正则表达式的核心详解
java·开发语言·信息可视化·正则表达式