4. 项目工程配置:Gradle 依赖、AndroidManifest 权限、BLE 相关特性声明
4.1 为什么工程配置是 DI + BLE 集成的基石
依赖注入框架与 BLE 组件的深度集成,从工程配置的第一步就已经开始。不正确的 Gradle 依赖声明会导致类加载失败、注解处理器不生效;遗漏或冗余的 AndroidManifest 权限会使 BLE 扫描/连接在运行时直接崩溃;缺少 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 库 | 注解处理 | 适用场景 |
|---|---|---|---|---|
| Koin + Nordic BLE | Koin 3.5.x | Nordic BLE 2.7.x | 无(纯 Kotlin) | 中小型项目、团队 Kotlin 栈统一 |
| Hilt + Nordic BLE | Dagger Hilt 2.50 | Nordic BLE 2.7.x | kapt / ksp | 大型项目、已有 Dagger 体系 |
| Koin + Android BLE | Koin 3.5.x | android.bluetooth 原生 | 无 | 极简项目、避免第三方 BLE 库 |
| Hilt + RxAndroidBle | Dagger Hilt 2.50 | RxAndroidBle 1.12.x | kapt / ksp | 响应式风格、RxJava 技术栈 |
4.3 AndroidManifest 权限配置
BLE 操作在 Android 平台上受到严格的权限管控。从 Android 6.0 到 Android 12+,权限模型经历了三次重要变更。下表列出所有与 BLE 相关的权限及其适用版本。
| 权限名称 | Android 版本 | 用途 | 是否需要运行时请求 |
|---|---|---|---|
BLUETOOTH | API 18+ | 基本蓝牙通信(连接、数据传输) | 否(安装时授权) |
BLUETOOTH_ADMIN | API 18+ | 蓝牙扫描、发现设备 | 否(安装时授权) |
ACCESS_FINE_LOCATION | API 23 – 31 | Android 6~11 扫描 BLE 设备需要精确定位 | 是(运行时) |
ACCESS_COARSE_LOCATION | API 23 – 31 | 部分设备可替代精确定位(扫描结果受限) | 是(运行时) |
BLUETOOTH_SCAN | API 31+ | Android 12+ 专用的蓝牙扫描权限 | 是(运行时) |
BLUETOOTH_CONNECT | API 31+ | Android 12+ 专用的蓝牙连接权限 | 是(运行时) |
BLUETOOTH_ADVERTISE | API 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 配置后,执行以下验证步骤确保配置正确生效:
- Gradle Sync:点击 Android Studio 的 Sync Now,观察 Build Output 窗口是否有依赖解析失败或版本冲突提示。
- 查看合并后的 Manifest:打开
app/build/intermediates/merged_manifests/debug/AndroidManifest.xml,确认所有 BLE 权限和特性声明已正确合并。 - Adb 权限检查:安装 debug APK 后执行
adb shell dumpsys package 包名 | grep permission,查看已授予的权限列表。 - 硬件特性检测:在应用启动日志中打印
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 项目从项目级配置到运行时验证的完整配置链路,以及各配置文件之间的依赖和声明关系。
4.7 常见错误与稳定性优化
| 错误现象 | 根本原因 | 解决方案 |
|---|---|---|
Gradle Sync 报 Failed to resolve: no.nordicsemi.android:ble | 仓库未添加或版本号错误 | 在 settings.gradle 中添加 google() 和 mavenCentral(),检查版本号是否存在 |
运行时 SecurityException: Need BLUETOOTH_SCAN permission | Android 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" 覆盖新权限 |
android.compileOptions.coreLibraryDesugaringEnabled true 以支持 Java 8+ 时间 API,避免 BLE 扫描回调中的时间戳处理异常;(2) 使用 android.bundle.language.enableSplit false 减少多语言资源包对 BLE 扫描延迟的偶发影响;(3) 在 debug 构建中保留 android:debuggable="true",release 构建务必移除。