工程结构与模块分层
android-bt-demo 是珠海杰理科技股份有限公司(Jieli)提供的 Android 端蓝牙测试示例代码集合仓库。本页说明仓库的整体工程组织方式、Gradle 模块划分、Java 包分层结构与各层职责边界。
目的与范围
本页聚焦 工程组织维度,回答以下问题:
- 仓库顶层包含哪些内容,为什么这样组织?
ATTConnect示例工程的 Gradle 构建结构(根工程与:app模块)是怎样的?com.jieli.bt.att下的包(tool.ble、util等)如何分层,各自承担什么职责?- 层与层之间如何协作,新增一个示例工程时如何扩展?
不属于本页范围(将由其他目录页单独说明):
- BLE 扫描、连接、数据收发的具体 API 与实现细节(见 BLE 连接管理相关页面)
- 具体设备/协议行为(GATT over BR/EDR 与 GATT over BLE 的差异)
概述
该仓库当前包含一个核心用例:ATT 设备(GATT over BR/EDR)的连接示例,演示 Android 端如何通过 GATT 协议与杰理蓝牙产品进行数据通讯。仓库采用「一个示例一个独立目录」的组织策略,每个示例目录是完整的、可独立导入 Android Studio 的 Gradle 工程。
从工程结构角度,整个仓库呈现三层结构:
- 仓库层(根目录):README 文档与许可证;
- 示例层(
ATTConnect/):完整的 Gradle 工程,含settings.gradle.kts与:app模块; - 代码层(
app/src/main/java/com.jieli.bt.att/):按职责划分的 Java 包结构。
支持环境与协议要求(摘自 README.md):
| 项目 | 说明 |
|---|---|
| 最低 Android 版本 | Android 5.0(API 21) |
| 目标版本 | Android 16+(API 36+) |
| 开发语言 | Kotlin / Java |
| 支持协议 | GATT over BR/EDR(ATT)、GATT over BLE |
架构
仓库与模块结构
flowchart TD
subgraph sg_Repo["android-bt-demo 仓库(根目录)"]
README["README.md / README_en.md"]
LICENSE["LICENSE(Apache 2.0)"]
end
subgraph sg_ATT["ATTConnect 示例工程"]
SETTINGS["settings.gradle.kts<br/>rootProject.name = BluetoothDemo"]
subgraph sg_App[":app 模块"]
SRC["src/main/java<br/>com.jieli.bt.att"]
LIBS["libs/*.aar 本地依赖"]
end
end
subgraph sg_BLE["tool.ble(BLE/ATT 核心能力层)"]
BLE_MGR["BleManager"]
BLE_CB["BleEventCallbackManager"]
BLE_THREAD["SendBleDataThread"]
BLE_IF["interfaces/<br/>回调与操作接口"]
BLE_MODEL["model/<br/>BleDevice、ScanDeviceInfo"]
end
subgraph sg_UTIL["util(通用工具层)"]
UTIL_BT["BluetoothUtil"]
UTIL_HEX["CHexConver"]
UTIL_UUID["UuidUtil"]
end
SETTINGS -->|"include(:app)"| SRC
SRC --> BLE_MGR
SRC --> UTIL_BT
BLE_MGR --> BLE_CB
BLE_MGR --> BLE_THREAD
BLE_MGR --> BLE_IF
BLE_MGR --> BLE_MODEL
结构说明:
- 根目录只承担仓库级职责:README 提供选型与快速开始指引,LICENSE 声明 Apache 2.0 协议。业务代码全部收拢在
ATTConnect/内,避免根目录被多个工程文件污染。 settings.gradle.kts将工程命名为BluetoothDemo并只包含一个:app模块,保证示例可独立打开、独立同步。:app模块内,libs/目录以本地 AAR 方式承载杰理蓝牙 SDK,代码层通过fileTree引入(见 app/build.gradle.kts)。tool.ble是示例的核心能力层:BleManager是设备管理入口,SendBleDataThread负责数据发送的线程调度,interfaces/抽象回调与操作契约,model/承载设备数据模型。util是纯工具层:蓝牙工具、十六进制转换、UUID 工具,被能力层复用,不依赖具体业务。
分层协作关系
flowchart LR
subgraph sg_UI["展示层"]
UI["Activity / 应用入口"]
end
subgraph sg_SVC["能力层(tool.ble)"]
MGR["BleManager"]
TH["SendBleDataThread"]
end
subgraph sg_IF["接口层(interfaces)"]
IF1["IBleOp / IBleEventCallback / 回调接口"]
end
subgraph sg_SYS["系统层"]
SYS["Android Bluetooth API<br/>(GATT over BR/EDR)"]
end
subgraph sg_MODEL["模型与工具层"]
MD["model:BleDevice、ScanDeviceInfo"]
UT["util:BluetoothUtil、CHexConver、UuidUtil"]
end
UI --> MGR
MGR --> IF1
IF1 --> SYS
SYS -.->|"系统回调"| MGR
MGR --> TH
MGR --> MD
MGR --> UT
设计意图:上层(UI)只与 BleManager 打交道,系统蓝牙 API 的差异被 interfaces/ 的抽象隔离;线程管理(发送队列)与数据模型分别独立成类,职责单一、便于替换与测试。
仓库顶层结构
根目录内容由 README.md 的工程结构章节 明确定义:
android-bt-demo/
├── ATTConnect/ # 📌 ATT 设备连接示例
├── LICENSE # Apache 2.0 开源协议
└── README.md
| 条目 | 类型 | 职责 |
|---|---|---|
ATTConnect/ | 独立 Gradle 工程 | ATT 设备(GATT over BR/EDR)连接示例,可单独导入 Android Studio |
LICENSE | 文件 | Apache 2.0 开源协议声明 |
README.md / README_en.md | 文档 | 概述、平台要求、快速开始、工程结构、示例选择指南 |
设计意图:仓库定位是「示例集合」,因此根目录刻意保持极简,每个示例以完整工程目录的形式平铺。这样客户可以按需拷贝单个示例目录,而不必携带整个仓库;同时避免多个示例共享一个 Gradle 工程导致构建耦合。README 中明确提示:ATTConnect 适合**双模蓝牙设备(经典蓝牙 + BLE)**通过 GATT 协议进行高速数据通讯的场景,核心类为 BtScanner(蓝牙扫描)与 BleManager(BLE 设备管理)。
Gradle 构建结构
根工程配置(settings.gradle.kts)
ATTConnect/settings.gradle.kts 中,仓库源通过 dependencyResolutionManagement 统一管理,并显式声明根工程名与模块:
dependencyResolutionManagement {
repositories {
// 仓库源策略:按组匹配放行 Google / AndroidX / 官方依赖
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
rootProject.name = "BluetoothDemo"
include(":app")
来源:ATTConnect/settings.gradle.kts(节选,非完整文件)
要点:
rootProject.name = "BluetoothDemo":示例工程在 IDE 中显示为BluetoothDemo,与仓库名android-bt-demo解耦——同一个工程可能被客户复制改名,工程名不应硬编码仓库名。include(":app"):当前仅一个应用模块;后续若引入 library 模块(如抽取 SDK 封装),在此追加include(":xxx")即可。includeGroupByRegex的白名单策略:仅放行 Google/AndroidX 官方组,其他依赖(如本地 AAR)不走远程仓库,避免依赖解析被外部源污染。
app 模块依赖(app/build.gradle.kts)
:app 模块通过 fileTree 引入 libs/ 目录下的本地 AAR 作为蓝牙 SDK 依赖:
dependencies {
implementation(fileTree(Pair("include", "*.aar"), Pair("dir", "libs")))
// ... 其余依赖
}
设计意图:杰理蓝牙 SDK 以 AAR 形式随示例分发,走本地文件依赖而非 Maven 仓库。好处是示例离线可用、版本随工程锁定;代价是升级 SDK 需要手动替换 libs/*.aar,这是「示例代码」定位下的合理取舍。
Java 包分层详解
代码统一位于 com.jieli.bt.att 命名空间下,分为两大包:tool.ble(能力层)与 util(工具层)。
tool.ble —— BLE/ATT 核心能力层
| 文件 | 角色 |
|---|---|
BleManager.java | 设备管理总入口:扫描、连接/断开、数据收发、GATT 服务发现的编排者 |
BleEventCallbackManager.java | 事件回调注册与分发管理,把系统/内部事件转发给上层 |
SendBleDataThread.java | 数据发送专用线程,串行化写操作,避免并发写导致 GATT 写入失败 |
interfaces/BleEventCallback.java | 事件回调接口(供上层实现) |
interfaces/IBleEventCallback.java | 内部事件回调契约 |
interfaces/IBleOp.java | 蓝牙操作抽象(扫描、连接、写等操作的定义) |
interfaces/IBtScanCallback.java | 扫描结果回调 |
interfaces/OnThreadStateListener.java | 发送线程状态监听 |
interfaces/OnWriteDataCallback.java | 写数据完成回调 |
model/BleDevice.java | 已连接/已发现设备的领域模型 |
model/ScanDeviceInfo.java | 扫描到的设备信息模型(RSSI、名称、地址等) |
分层逻辑:
- 接口层(
interfaces/):把「扫描、连接、写数据、线程状态」等异步操作全部抽象为接口。上层代码依赖接口而非具体实现,是替换底层蓝牙实现(例如从 BR/EDR 切到 BLE)的接缝点。 - 管理器层(
BleManager、BleEventCallbackManager):BleManager对外提供门面式 API;BleEventCallbackManager负责回调的注册与派发,让回调只投递到已注册的监听者。 - 线程层(
SendBleDataThread):GATT 写入在 Android 上对时序敏感,数据发送被放入专用线程串行执行,并用OnWriteDataCallback反馈每包写入结果,防止上层疯狂调用导致指令交错。 - 模型层(
model/):BleDevice与ScanDeviceInfo分离「已连接设备」与「扫描结果」两种语义,避免把扫描临时信息混入设备状态。
util —— 通用工具层
| 文件 | 职责 |
|---|---|
BluetoothUtil.java | 系统蓝牙相关封装(适配器获取、状态判断等) |
CHexConver.java | 十六进制与字节数组转换,供协议数据收发使用 |
UuidUtil.java | GATT Service/Characteristic UUID 的解析与生成 |
设计意图:工具层不依赖 tool.ble 的任何类型,可独立复用与单元测试;CHexConver 单独成类是因为杰理协议栈大量以 hex 字符串形式交互,属于高频公共操作。
核心流程
下图展示数据从上层入口到系统蓝牙 API 再到回调返回的完整时序:
sequenceDiagram
participant App as 应用入口(Activity)
participant MGR as BleManager
participant IF as interfaces 回调/操作接口
participant SYS as Android Bluetooth API
participant TH as SendBleDataThread
App->>MGR: 发起扫描/连接请求
MGR->>IF: 调用 IBleOp 抽象操作
IF->>SYS: 系统蓝牙 API(GATT over BR/EDR)
SYS-->>MGR: 设备发现 / 连接状态事件
MGR-->>App: 经 BleEventCallbackManager 分发回调
App->>MGR: 写入业务数据
MGR->>TH: 投递写任务(串行队列)
TH-->>MGR: 写完成(OnWriteDataCallback)
MGR-->>App: 通知发送结果
流程要点:
- 异步为主:扫描、连接、写入全部走回调,
BleEventCallbackManager是上层感知事件变化的唯一通道; - 写入串行化:所有写操作经
SendBleDataThread排队执行,规避 Android GATT 对并发写入的限制; - 状态可监听:
OnThreadStateListener暴露线程生命周期,便于上层在断开/重连时复位发送队列。
使用示例
示例一:按 README 指引选择并导入示例
README 在快速开始章节直接给出了目录选择方式,客户按需拷贝 ATTConnect/ 目录即可:
android-bt-demo/
├── ATTConnect/ # ATT 设备(GATT over BR/EDR)连接示例
└── .../ # 更多示例持续更新中
来源:README.md
随后在 Android Studio 中 File → Open 选择 ATTConnect/ 目录,等待 Gradle 同步后运行 app。这正是「一个示例一个独立工程」结构的直接收益:客户无需理解整个仓库即可单目录构建。
示例二:声明模块与工程名
新增或确认模块时,在 ATTConnect/settings.gradle.kts 中维护:
rootProject.name = "BluetoothDemo"
include(":app")
rootProject.name 独立于目录名设计,使客户把 ATTConnect/ 复制重命名后(如 MyBluetoothDemo/)工程显示名依然稳定。
示例三:引入本地蓝牙 SDK
在 :app 模块的 dependencies 块中引入 libs/ 下的 AAR:
dependencies {
implementation(fileTree(Pair("include", "*.aar"), Pair("dir", "libs")))
}
该写法将 app/libs/ 目录下所有 .aar 一次性纳入编译,升级 SDK 只需替换文件并重新同步。
配置项
以下配置均来源于仓库内实际文件(README 与 Gradle 脚本):
| 配置项 | 类型 | 默认值/当前值 | 说明 |
|---|---|---|---|
| 最低 Android 版本 | 系统要求 | Android 5.0(API 21) | 仓库支持的设备下限 |
| 目标 Android 版本 | 系统要求 | Android 16+(API 36+) | 编译与运行目标 |
| 开发语言 | 系统要求 | Kotlin / Java | 示例以 Java 实现(com.jieli.bt.att 下为 .java 文件) |
rootProject.name | Gradle | BluetoothDemo | 示例工程显示名称 |
| 模块列表 | Gradle | :app | 当前唯一模块 |
| SDK 依赖方式 | Gradle | libs/*.aar 本地文件 | implementation(fileTree(...)) 引入 |
| 远程仓库策略 | Gradle | 仅放行 com.android.*、com.google.*、androidx.* | includeGroupByRegex 白名单 |
| 支持协议 | 功能 | GATT over BR/EDR(ATT)、GATT over BLE | 当前示例侧重 ATT |
说明:
compileSdk/minSdk/targetSdk的具体数值声明位于ATTConnect/app/build.gradle.kts的android {}块中;由于本次取证未展开该文件全部内容,上表以 README 声明的系统要求为准。详细配置请直接查看 app/build.gradle.kts。
失败模式与边界情况
- ATT 协议兼容性风险:README 明确提示「Android 端对 ATT(GATT over BR/EDR)功能的支持可能存在兼容性问题,建议在目标设备上进行充分测试」。因此工程将蓝牙操作全部抽象到
interfaces/,正是为了在系统行为不一致时能够替换底层实现而不用改动上层。 - 异步回调丢失:扫描/连接/写入均为异步回调,若上层在回调到达前销毁(如页面退出),
BleEventCallbackManager需要保证监听器解绑,避免空引用或事件泄漏——这是所有管理器类共有的边界约束。 - 并发写入冲突:Android GATT 不保证并发写安全,工程以
SendBleDataThread串行化写操作规避;若上层绕过该线程直接写,可能触发GattStatus写入失败。 - 依赖解析边界:
settings.gradle.kts用正则白名单限制远程仓库,若未来引入非 Google/AndroidX 的远程依赖(如第三方 Maven 库),需要显式补充放行规则,否则同步失败。
扩展点
- 新增示例工程:在仓库根目录新建独立目录(如
BleConnect/),复制ATTConnect的工程骨架(settings.gradle.kts、app/),保持「一目录一工程」约定即可,无需改动现有示例。 - 替换/扩展蓝牙实现:
interfaces/下的IBleOp、IBleEventCallback、IBtScanCallback等接口即接缝;若要支持新的传输类型或自定义协议栈,实现这些接口并在BleManager中切换即可。 - 升级 SDK:替换
app/libs/*.aar文件,Gradle 同步后自动生效;CHexConver、UuidUtil等工具类可保持稳定,降低升级成本。 - 增加模块:若将 SDK 封装抽取为独立库,在
settings.gradle.kts追加include(":sdk"),并在:app中声明项目依赖。
相关链接
- README.md(仓库总览与快速开始)
- ATTConnect/settings.gradle.kts(模块声明与仓库策略)
- ATTConnect/app/build.gradle.kts(模块配置与依赖)
- BleManager.java(能力层入口)
- interfaces 回调接口目录
- ATTConnect 详细说明(示例级文档,见 README 引用)
注:能力层(
tool.ble)中各类的具体方法签名与调用时序,请参阅 BLE 连接管理与数据收发相关目录页;本页仅从工程结构与分层角度进行说明。