1. Android蓝牙开发环境搭建

本章聚焦 Android 蓝牙开发的初始步骤:正确配置 SDK 版本、理解模拟器与真机的行为差异、获取 BluetoothAdapter 对象,并避开最常见的开发陷阱。所有后续蓝牙通信与本地存储的章节都依赖于本章搭建的基础环境。

1.1 SDK 版本配置要点

Android 蓝牙 API 在 API 级别 18(Android 4.3)进行了重大重写。目前主流的开发目标应至少覆盖 API 21(Android 5.0)以上,但要注意最低版本对经典蓝牙和低功耗蓝牙(BLE)的支持差异。

蓝牙类型最低 API Level建议 targetSdkVersion核心类
经典蓝牙 (RFCOMM)API 5 (Android 2.0)≥ 29 (Android 10)BluetoothDevice, BluetoothSocket
低功耗蓝牙 (BLE)API 18 (Android 4.3)≥ 31 (Android 12)BluetoothLeScanner, BluetoothGatt
蓝牙 LE Audio (新)API 33 (Android 13)≥ 34 (Android 14)BluetoothLeAudio
编译配置注意点: 如果项目使用 compileSdkVersion 34 但 minSdkVersion 21,仍然可以使用蓝牙 RFCOMM 和 BLE,但需要运行时检查 Android 版本,避免调用仅在 API 33+ 上可用的方法。

build.gradle (Module: app) 典型配置

android {
    compileSdk 34

    defaultConfig {
        applicationId "com.example.ble_storage_demo"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }
    ...
}

dependencies {
    // 蓝牙 BLE 扫描(可选)
    implementation "androidx.activity:activity:1.9.1"
}

1.2 模拟器 vs 真机 —— 关键差异

绝大多数 Android 模拟器(包括 Android Studio 自带模拟器和 Genymotion)不支持蓝牙硬件模拟。即使使用“扩展控制”中的“蓝牙”设置,也只是虚拟界面,无法真实打开或关闭射频。

对比维度模拟器真机
BluetoothAdapter 是否可用返回 null(多数情况)返回有效实例
蓝牙开关控制不可调用 enable()/disable()(会抛异常或静默失败)正常控制并弹出系统对话框
设备发现 / 扫描无真实扫描结果;模拟器会返回空列表或固定测试数据返回周围真实设备
建立连接 (RFCOMM/GATT)无法建立物理连接;连接回调会超时报错正常建立
位置权限要求 (BLE)仍然需要 ACCESS_FINE_LOCATION 声明,但不会触发实时位置请求(Android 12+ 已改)必须动态请求位置权限,否则 BLE 扫描无结果
开发调试建议仅用于 UI 测试、权限弹窗逻辑验证蓝牙功能测试唯一有效环境
开发建议: 使用真机 + USB 调试或 Wi‑Fi ADB 作为主要调试手段。如果必须使用模拟器,可考虑安装第三方蓝牙虚拟驱动(如 Bluetooth LE Simulator),但仅适用于 BLE 通信的 UI 逻辑验证,不适用于物理连接测试。

1.3 获取 BluetoothAdapter 的标准方式

BluetoothAdapter 代表本地蓝牙设备。获取实例必须通过系统服务 BluetoothManager,从 Android 4.3 开始推荐这种做法,可以保证在高版本系统上的兼容性。

推荐实现流程

  1. 在 AndroidManifest.xml 中声明必要权限(见下文)。
  2. 调用 Context.getSystemService(Context.BLUETOOTH_SERVICE) 获取 BluetoothManager。
  3. 通过 bluetoothManager.getAdapter() 获取 BluetoothAdapter 实例。
  4. 检查返回是否为 null —— 如果为 null 则表示设备不支持蓝牙(或者模拟器无蓝牙驱动)。

完整代码片段

private BluetoothAdapter getBluetoothAdapter(Context context) {
    BluetoothManager manager = (BluetoothManager) context
            .getSystemService(Context.BLUETOOTH_SERVICE);
    if (manager == null) {
        return null; // 系统服务不可用(极少情况)
    }
    return manager.getAdapter();
}

// 使用示例
BluetoothAdapter adapter = getBluetoothAdapter(context);
if (adapter == null) {
    // 设备不支持蓝牙(或模拟器)
    Toast.makeText(context, "此设备不支持蓝牙", Toast.LENGTH_LONG).show();
    return;
}
if (!adapter.isEnabled()) {
    // 启动蓝牙(通常需要用户确认)
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

AndroidManifest.xml 权限声明

<!-- 经典蓝牙 (API 5+) -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- BLE (API 18+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- 定位(用于 BLE 扫描,Android 12+ 已弱化,但低版本仍需) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
    android:maxSdkVersion="30" />
<!-- 如果 app 也需要网络/蓝牙进行位置推测,可以声明粗略位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
    android:maxSdkVersion="30" />

<!-- 硬件特性声明(可选,用于阻止不支持蓝牙的设备安装) -->
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />

1.4 常见坑点与调试方法

  1. 模拟器 getAdapter() 返回 null
    这是新手最容易遇见的“死结”。请务必在真机上测试蓝牙功能。如果必须用模拟器,可加入条件判断代码使 App 降级运行(不崩溃)。
  2. Android 12+ (API 31) 权限拆分
    从 Android 12 开始,蓝牙扫描权限需要动态请求 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT,而不再是依赖位置权限。如果在 targetSdk >= 31 的 app 中只请求位置权限,扫描将无结果。
  3. 未打开蓝牙时调用 startDiscovery() 导致 IllegalStateException
    必须先检查 adapter.isEnabled(),如果未启用,通过 ACTION_REQUEST_ENABLE 引导用户打开。
  4. Android 10 限制非系统应用扫描
    从 Android 10 开始,普通应用无法扫描到类型为“不可发现”的经典蓝牙设备,BLE 扫描也受后台扫描限制。需要申请 ACCESS_BACKGROUND_LOCATION(Android 10-11)或使用前台服务。
  5. 重复获取 BluetoothAdapter 导致内存泄漏
    BluetoothAdapter 是单例,每次调用 BluetoothAdapter.getDefaultAdapter() 或 manager.getAdapter() 返回的都是同一个对象,无需缓存。但若在 Activity 或 Fragment 中频繁调用系统服务,注意不要在非 UI 线程长期持有 Context 引用。
调试日志查看技巧: 蓝牙相关的日志 Tag 通常为 BluetoothAdapter、BluetoothManager、BluetoothDevice、BluetoothGatt。使用命令 adb logcat -s BluetoothAdapter:V BluetoothManager:V BluetoothGatt:V 查看详细状态。

1.5 环境搭建流程图

下面的 SVG 流程图展示了从配置 SDK 到成功获取 BluetoothAdapter 并开始使用的整体步骤:

Bluetooth开发环境搭建流程图 第1章:蓝牙开发环境搭建流程 1. 配置build.gradle (compileSdk 34, minSdk 21) 2. 声明权限 (BLUETOOTH, BLUETOOTH_ADMIN, BLUETOOTH_SCAN等) 3. 获取 BluetoothManager (getSystemService(BLUETOOTH_SERVICE)) 4. 获取 BluetoothAdapter (manager.getAdapter()) adapter==null? 是 设备不支持/模拟器 否 isEnabled() ? 未启用 ACTION_REQUEST_ENABLE 已启用 开始蓝牙操作 注意:模拟器返回null

1.6 小结

通过本章的学习,你已经掌握了蓝牙开发的基本环境配置步骤:

  • 正确设置 compileSdk 与 minSdk,并加入必要的蓝牙权限声明。
  • 认清模拟器无法模拟蓝牙硬件的限制,坚持使用真机测试。
  • 使用 BluetoothManager.getAdapter() 获取 BluetoothAdapter 实例,并进行 null 检查与开关状态处理。
  • 避开常见的权限、版本兼容和错误处理陷阱。