4. 项目工程配置:Gradle 依赖、AndroidManifest 权限、BLE 相关特性声明

4.1 为什么工程配置是 DI + BLE 集成的基石

依赖注入框架与 BLE 组件的深度集成,从工程配置的第一步就已经开始。不正确的 Gradle 依赖声明会导致类加载失败、注解处理器不生效;遗漏或冗余的 AndroidManifest 权限会使 BLE 扫描/连接在运行时直接崩溃;缺少 BLE 特性声明则可能让部分手机无法识别设备的蓝牙能力。本章围绕「声明式配置」这一主线,依次拆解项目级、模块级、清单级的配置要点,并为后续章节的代码注入和运行时管理建立可靠的工程环境。

配置核心理念: 依赖声明遵循「最小必需原则」——只引入 DI 框架和 BLE 库的必要模块,权限声明遵循「精准匹配原则」——按目标 Android 版本选择对应的蓝牙权限模型,特性声明遵循「显式告知原则」——在 manifest 中明确告知系统当前应用使用 BLE 的硬件特性。

4.2 Gradle 依赖配置层级

一个标准的 Android 项目包含两级 Gradle 配置:项目级 settings.gradle 声明模块结构和仓库地址,模块级 build.gradle 声明具体依赖和插件。BLE + DI 集成需要在这两级中分别添加对应条目。

4.2.1 项目级 settings.gradle

确保 pluginManagement 和 dependencyResolutionManagement 包含 Google Maven、Maven Central 以及 DI 框架和 BLE 库所需的仓库源。

// settings.gradle (项目根目录)
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        // 某些 BLE 库或 DI 框架的 snapshot 版本可能需要额外仓库
        // maven { url 'https://jitpack.io' }
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // 如果使用 Kotlin BLE 库或 Koin snapshot
        // maven { url 'https://kotlin.bintray.com/kotlinx' }
    }
}

rootProject.name = "BLE-DI-Demo"
include ':app'

4.2.2 模块级 build.gradle

模块级配置包含插件声明、Android SDK 版本、依赖声明三个核心部分。以下展示集成 Koin(轻量 DI)+ Nordic BLE 库的典型配置。

// app/build.gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    // 如果使用 Hilt,还需添加 id 'dagger.hilt.android.plugin'
    // 如果使用 KSP 加速注解处理,添加 id 'com.google.devtools.ksp'
}

android {
    namespace 'com.example.bledi.demo'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.bledi.demo"
        minSdk 21   // BLE 从 Android 4.3 (API 18) 开始支持,但建议 minSdk >= 21
        targetSdk 34
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildFeatures {
        viewBinding true   // 配合 DI 注入 View 时推荐开启
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '17'
    }
}

dependencies {
    // ─── DI 框架 (二选一,切勿混用) ───
    // 方案 A: Koin (无注解,纯 Kotlin)
    implementation 'io.insert-koin:koin-android:3.5.3'
    implementation 'io.insert-koin:koin-androidx-compat:3.5.3'
    // Koin 单元测试支持
    testImplementation 'io.insert-koin:koin-test:3.5.3'

    // 方案 B: Hilt (注解驱动,需配合 kapt/ksp)
    // implementation 'com.google.dagger:hilt-android:2.50'
    // kapt 'com.google.dagger:hilt-compiler:2.50'
    // implementation 'androidx.hilt:hilt-work:1.1.0'
    // kapt 'androidx.hilt:hilt-compiler:1.1.0'

    // ─── BLE 通信库 (推荐 Nordic BLE 或 Android BLE 原生封装) ───
    implementation 'no.nordicsemi.android:ble:2.7.2'
    // BLE 扫描扩展
    implementation 'no.nordicsemi.android:ble-common:2.7.2'
    // 如果使用 Kotlin Flow 包装
    implementation 'no.nordicsemi.android:ble-flow:2.7.2'

    // ─── Android 核心库 ───
    implementation 'androidx.core:core-ktx:1.12.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'

    // ─── 权限管理库 (可选,简化运行时权限请求) ───
    implementation 'com.google.accompanist:accompanist-permissions:0.34.0'
}
常用 DI 框架 + BLE 库依赖组合对比
组合方案DI 框架BLE 库注解处理适用场景
Koin + Nordic BLEKoin 3.5.xNordic BLE 2.7.x无(纯 Kotlin)中小型项目、团队 Kotlin 栈统一
Hilt + Nordic BLEDagger Hilt 2.50Nordic BLE 2.7.xkapt / ksp大型项目、已有 Dagger 体系
Koin + Android BLEKoin 3.5.xandroid.bluetooth 原生无极简项目、避免第三方 BLE 库
Hilt + RxAndroidBleDagger Hilt 2.50RxAndroidBle 1.12.xkapt / ksp响应式风格、RxJava 技术栈
依赖冲突预警: Koin 和 Hilt 不能同时出现在同一模块的 dependencies 中,否则会在运行时产生「Module重复定义」或「Scope冲突」异常。如果进行框架迁移,必须彻底移除旧框架的所有依赖、import 和 Module 声明后再引入新框架。

4.3 AndroidManifest 权限配置

BLE 操作在 Android 平台上受到严格的权限管控。从 Android 6.0 到 Android 12+,权限模型经历了三次重要变更。下表列出所有与 BLE 相关的权限及其适用版本。

BLE 权限清单与 Android 版本适配
权限名称Android 版本用途是否需要运行时请求
BLUETOOTHAPI 18+基本蓝牙通信(连接、数据传输)否(安装时授权)
BLUETOOTH_ADMINAPI 18+蓝牙扫描、发现设备否(安装时授权)
ACCESS_FINE_LOCATIONAPI 23 – 31Android 6~11 扫描 BLE 设备需要精确定位是(运行时)
ACCESS_COARSE_LOCATIONAPI 23 – 31部分设备可替代精确定位(扫描结果受限)是(运行时)
BLUETOOTH_SCANAPI 31+Android 12+ 专用的蓝牙扫描权限是(运行时)
BLUETOOTH_CONNECTAPI 31+Android 12+ 专用的蓝牙连接权限是(运行时)
BLUETOOTH_ADVERTISEAPI 31+Android 12+ 蓝牙广播权限(外设模式)是(运行时)

以下是一个兼容 Android 12+ 和旧版本的完整权限声明示例:

<!-- AndroidManifest.xml -->
<!-- 旧版本必需 (API 18+) -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- Android 6 – 11 (API 23~30) 扫描需要位置权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
    android:maxSdkVersion="30" />

<!-- Android 12+ (API 31+) 新式蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 如果应用作为 BLE 外设(广播模式),还需要 -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> -->

<!-- 后台扫描权限 (可选,需要额外申请) -->
<!-- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> -->
最佳实践: 使用 android:maxSdkVersion="30" 限制旧权限仅在 API 30 及以下生效,避免 Android 12+ 设备上弹出冗余的位置权限请求。同时,在运行时权限请求时,根据 Build.VERSION.SDK_INT 动态选择请求 BLUETOOTH_SCAN / BLUETOOTH_CONNECT 还是 ACCESS_FINE_LOCATION。

4.4 BLE 相关特性声明

除了权限,还必须在 <uses-feature> 中声明 BLE 硬件特性。这能告知 Google Play 商店和应用框架:当前应用依赖 BLE 能力,从而在无 BLE 硬件的设备上隐藏或过滤应用。

<!-- AndroidManifest.xml 中声明 BLE 特性 -->
<!-- BLE 核心特性 (必需) -->
<uses-feature android:name="android.hardware.bluetooth_le"
    android:required="true" />

<!-- 如果应用也需要经典蓝牙,可额外声明 -->
<!-- <uses-feature android:name="android.hardware.bluetooth" android:required="false" /> -->

<!-- 声明支持的 BLE 角色 (可选,用于过滤) -->
<!-- central 角色 (手机作为主机连接外设) -->
<uses-feature android:name="android.hardware.bluetooth_le.central"
    android:required="true" />
<!-- peripheral 角色 (手机作为外设广播) -->
<!-- <uses-feature android:name="android.hardware.bluetooth_le.peripheral"
    android:required="false" /> -->

如果应用在无 BLE 硬件的平板或电视上仍可降级运行(例如仅显示 UI 教程),将 android:required="false" 并在代码中通过 PackageManager.hasSystemFeature() 做运行时检测。

4.5 配置验证与调试

完成 Gradle 和 Manifest 配置后,执行以下验证步骤确保配置正确生效:

  1. Gradle Sync:点击 Android Studio 的 Sync Now,观察 Build Output 窗口是否有依赖解析失败或版本冲突提示。
  2. 查看合并后的 Manifest:打开 app/build/intermediates/merged_manifests/debug/AndroidManifest.xml,确认所有 BLE 权限和特性声明已正确合并。
  3. Adb 权限检查:安装 debug APK 后执行 adb shell dumpsys package 包名 | grep permission,查看已授予的权限列表。
  4. 硬件特性检测:在应用启动日志中打印 PackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) 的结果。
// 运行时硬件特性检测示例 (Kotlin)
val hasBle = packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
Log.d("BleConfig", "Device supports BLE: $hasBle")
if (!hasBle) {
    // 提示用户该设备不支持 BLE 操作
    Toast.makeText(this, "此设备不支持低功耗蓝牙", Toast.LENGTH_LONG).show()
}
调试利器: 在 AndroidManifest.xml 的 <application> 标签中设置 android:debuggable="true"(仅 debug 构建),配合 adb shell dumpsys bluetooth_manager 可以查看当前蓝牙状态、已绑定设备和 BLE 扫描会话,快速定位配置层面的问题。

4.6 配置架构总览

下图展示 BLE + DI 项目从项目级配置到运行时验证的完整配置链路,以及各配置文件之间的依赖和声明关系。

BLE + DI 项目工程配置架构图 BLE+DI 项目工程配置架构图 settings.gradle (项目级) app/build.gradle (模块级) DI 框架依赖 (Koin/Hilt) BLE 通信库依赖 (Nordic等) AndroidManifest.xml 蓝牙权限声明 (BLE Scan/Connect) BLE 特性声明 (hardware.bluetooth_le) Gradle Sync & Build 验证 配置链路由上至下逐级声明,下层依赖上层的仓库、插件和 SDK 版本;Sync & Build 是最终验证关卡

4.7 常见错误与稳定性优化

工程配置常见错误及解决方案
错误现象根本原因解决方案
Gradle Sync 报 Failed to resolve: no.nordicsemi.android:ble仓库未添加或版本号错误在 settings.gradle 中添加 google() 和 mavenCentral(),检查版本号是否存在
运行时 SecurityException: Need BLUETOOTH_SCAN permissionAndroid 12+ 未声明或未请求新式蓝牙权限在 Manifest 中添加 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT,并在代码中运行时请求
Google Play 提示「此应用与您的设备不兼容」uses-feature 声明 required="true" 但设备无 BLE将 required="false" 并在代码中做降级处理,或针对 BLE 设备发布单独 APK
DI 框架注入 BLE 服务时抛出 NoBeanDefFoundException依赖声明顺序导致 BLE 库未在 DI 模块启动前加载确保 startKoin() 或 @HiltAndroidApp 在 Application.onCreate 中优先于 BLE 初始化
编译报 Duplicate class 冲突Koin 和 Hilt 的依赖同时存在,或 BLE 库传递依赖与 DI 框架冲突执行 ./gradlew :app:dependencies 分析依赖树,使用 exclude 或 implementation (""){ { exclude group: "冲突组" } 解决
APP 安装后无法扫描到任何 BLE 设备Android 12+ 缺少 BLUETOOTH_SCAN 权限,或旧版本缺少位置权限检查 adb shell dumpsys package 包名 | grep permission,确认权限已授予;对于 Android 12+ 确保未误声明 maxSdkVersion="30" 覆盖新权限
性能稳定性建议: (1) 在 build.gradle 中启用 android.compileOptions.coreLibraryDesugaringEnabled true 以支持 Java 8+ 时间 API,避免 BLE 扫描回调中的时间戳处理异常;(2) 使用 android.bundle.language.enableSplit false 减少多语言资源包对 BLE 扫描延迟的偶发影响;(3) 在 debug 构建中保留 android:debuggable="true",release 构建务必移除。

4.8 本章小结