应用入口与启动流程
本文档介绍 JL OTA HarmonyOS 示例应用的入口与启动链路:从 module.json5 的模块声明、EntryAbility(UIAbility)的完整生命周期,到启动页 LaunchPage 的隐私协议闸门与主流程跳转,以及启动阶段对偏好设置、蓝牙实例与 OTA 管理器的初始化。
Purpose and Scope
本页面覆盖该 OTA 示例应用 入口与启动 这一完整能力,具体包括:
entry模块的module.json5配置(mainElement、页面表、桌面skills、蓝牙与网络权限声明);EntryAbility的生命周期方法及其在onWindowStageCreate中执行的全屏设置与init()初始化;- 启动页
LaunchPage的用户协议检查、隐私弹窗与进入主页面流程; - 启动阶段对
PreferencesHelper(偏好设置)、bluetoothInstance(蓝牙管理器)与bluetoothOTAManager(OTA 管理器)的装配。
以下相关主题属于兄弟页面,本文不展开:蓝牙扫描/连接/数据收发的具体实现(BLE/SPP)、OTA 升级协议细节、日志工具内部实现。如需了解,请参见对应目录页面。
Overview
JL OTA 示例应用是一个基于蓝牙(BLE/SPP)的固件升级工具,入口模块为 entry。系统启动应用时,根据 module.json5 中声明的 mainElement 拉起 EntryAbility(继承自 UIAbility),随后依次执行:
onCreate()—— 绑定PreferencesHelper上下文,并根据用户偏好恢复日志落盘开关;onWindowStageCreate()—— 将窗口设置为全屏,调用init()完成业务初始化,并加载首屏页面pages/LaunchPage;LaunchPage.onPageShow()—— 读取user_agreement_status偏好:已同意协议则直接tryToEnterMainPage()进入主页面,否则弹出隐私协议对话框,等待用户确认。
设计意图:将"用户协议确认"作为进入主流程的强制闸门,确保合规要求;同时把蓝牙与 OTA 的初始化放在 Ability 生命周期内,保证用户进入主页面时 SDK 已经就绪,避免首屏出现"设备未就绪"的竞态。
Architecture
flowchart TD
subgraph sg_Config["配置层(module.json5)"]
MainElement["mainElement: EntryAbility"]
PagesCfg["pages: $profile:main_pages"]
Permissions["requestPermissions: 蓝牙 / 网络 / WiFi 权限"]
Skills["skills: entity.system.home"]
end
subgraph sg_Ability["Ability 层(EntryAbility.ets)"]
OnCreate["onCreate()"]
OnWindowStageCreate["onWindowStageCreate()"]
Init["init()"]
OnDestroy["onWindowStageDestroy()"]
end
subgraph sg_Init["启动初始化"]
PrefHelper["PreferencesHelper"]
BleMgr["bluetoothInstance(BluetoothManager)"]
OtaMgr["bluetoothOTAManager"]
LogMgr["Log(日志模块)"]
end
subgraph sg_Page["页面层"]
LaunchPage["LaunchPage(启动页)"]
Dialog["隐私协议弹窗(CustomContentDialog)"]
MainPage["主页面"]
end
MainElement -->|"拉起"| OnCreate
Skills -->|"桌面图标触发"| MainElement
OnCreate --> PrefHelper
OnCreate --> LogMgr
OnWindowStageCreate --> Init
OnWindowStageCreate --> LaunchPage
Init --> PrefHelper
Init --> BleMgr
Init --> OtaMgr
LaunchPage -->|"onPageShow 检查 user_agreement_status"| Dialog
Dialog -->|"用户同意"| MainPage
LaunchPage -->|"已同意,直接进入"| MainPage
架构说明:
- 配置层
module.json5是启动链路的源头:mainElement指向EntryAbility,skills声明entity.system.home/action.system.home使应用可从桌面图标启动,requestPermissions预先声明蓝牙发现、使用、访问以及网络权限。 - Ability 层 是启动的核心协调者:
onCreate只做轻量上下文绑定,真正的业务初始化集中在onWindowStageCreate中的init(),与页面加载并行执行。 - 启动初始化 将用户偏好(MTU、通信方式、鉴权/重连选项)应用到蓝牙与 OTA 管理器实例,使 SDK 配置与用户上次选择保持一致。
- 页面层 的
LaunchPage承担协议闸门职责:onPageShow每次出现都会校验协议状态,未同意时以autoCancel: false的模态弹窗强制用户确认。
主内容:入口配置(module.json5)
entry/src/main/module.json5 是 HarmonyOS 模块清单,声明了入口 Ability 与所需权限:
name: "entry"、type: "entry"—— 应用唯一入口模块;mainElement: "EntryAbility"—— 应用启动时默认拉起的 Ability;deviceTypes: ["phone", "tablet"]—— 支持手机与平板;pages: "$profile:main_pages"—— 页面路由表由main_pages.json配置文件维护;abilities中EntryAbility的srcEntry指向./ets/entryability/EntryAbility.ets,startWindowIcon使用透明图标ic_transparent,startWindowBackground使用start_window_background颜色资源,保证启动窗口与启动页视觉衔接;removeMissionAfterTerminate: true—— 任务终止后从任务栈移除;skills声明桌面入口,exported: true允许系统拉起;requestPermissions声明 5 项权限:ACCESS_BLUETOOTH(含使用场景inuse)、DISCOVER_BLUETOOTH、USE_BLUETOOTH、INTERNET、GET_WIFI_INFO。
设计意图:启动窗口使用透明图标 + 纯色背景,避免桌面图标与启动页之间出现视觉闪变;蓝牙权限按需在
EntryAbility使用期间(inuse)申请,符合最小权限原则。
主内容:EntryAbility 生命周期
EntryAbility 继承自 UIAbility,是应用进程内唯一的能力入口。它通过 7 个生命周期回调组织启动与退出流程,其职责划分如下表:
| 生命周期回调 | 职责 |
|---|---|
onCreate | 绑定 PreferencesHelper 上下文;异步读取 LOG_SAVING_SWITCH,开启日志落盘 |
onDestroy | 记录退出日志 |
onWindowStageCreate | 设置全屏;调用 init();加载 pages/LaunchPage |
init() | 读取 APP_SETTINGS 偏好,应用到蓝牙/OTA 管理器 |
onWindowStageDestroy | 根据开关异步停止日志落盘 |
onForeground / onBackground | 记录前后台切换日志 |
onCreate:上下文绑定与日志恢复
onCreate 是整个启动链路的第一个业务代码执行点。它首先把 this.context 注入 PreferencesHelper(单例),随后异步读取用户是否开启了日志保存开关;若开启则调用 Log.startSaving(this.context) 恢复日志落盘。该步骤刻意保持轻量,避免阻塞 Ability 创建。
onWindowStageCreate 与 init:窗口与业务初始化
窗口创建后,应用先通过 windowStage.getMainWindow 将主窗口设为全屏,然后并行执行两件事:调用 init() 完成 SDK 装配,以及 loadContent('pages/LaunchPage') 加载首屏。
init() 的初始化顺序经过精心设计,体现依赖方向:
- 读取
APP_SETTINGS(AppOptions); - 通过
bluetoothInstance.bleImpl获取连接配置,将mtu覆盖为用户偏好(默认512)后写回 —— 先改配置再使用; - 根据
appOptions.communication设置通信方式:SPP对应"EDR",其余为"BLE"; - 调用
bluetoothOTAManager.init()初始化 OTA 管理器; - 通过
getOTAWrapperOption()注入两个策略回调:isUseAuth(默认true)与isInnerReconnect(默认false),供 SDK 在升级流程中询问上层决策。
设计意图:
isUseAuth/isInnerReconnect以函数闭包而非固定值注入,使 SDK 在每次需要决策时读取最新的用户偏好,而非启动时的一次性快照,这是"策略模式"的典型用法。
主内容:启动页 LaunchPage 与隐私协议闸门
LaunchPage 是应用的首屏 @Entry 组件,承担两项职责:展示品牌启动画面,以及作为"用户协议确认"的强制闸门。
页面数据准备(aboutToAppear)
在组件即将出现时,通过 getContext(this).resourceManager 同步读取三个字符串资源,拼装出协议文案(user_agreement、privacy_policy、statement2)。使用同步 getStringSync 是因为这些字符串是构建弹窗 UI 所必需的静态文案,读取成本低、无 IO 阻塞风险。
协议状态检查(onPageShow)
onPageShow 是协议闸门的判定点:每次页面显示(包括从弹窗/其他页面返回)都会读取 KEY_USER_AGREEMENT(即 "user_agreement_status")偏好:
- 偏好值为
true→ 直接tryToEnterMainPage()进入主页面; - 偏好值为
false或不存在(首次启动)→dialogController.open()弹出隐私协议对话框。
设计意图:把判定放在
onPageShow而非aboutToAppear,是为了覆盖"用户从协议弹窗返回、或从主页面返回启动页"等场景,保证闸门在任何路径下都生效。首次启动时偏好不存在,走else分支弹窗,天然覆盖"新用户必须同意协议"的合规场景。
隐私协议弹窗
弹窗通过 CustomDialogController 实现,关键配置:
cornerRadius: 12—— 圆角样式;autoCancel: false—— 禁止点击遮罩关闭,强制用户做出明确选择;onWillDismiss: () => {}—— 空实现,拦截系统默认关闭行为;builder: CustomContentDialog(...)—— 使用CustomContentDialog自定义内容构建器。
buildPrivacyContent() 构建器在 Column 中展示协议标题、声明文案与两个可点击的协议链接(Span(...).onClick(...) 跳转到 AgreementUrl.USER_AGREEMENT 等网页)。弹窗底部提供"同意/不同意"操作(位于文件后半部分),同意后写回偏好并调用 tryToEnterMainPage()。
启动画面
build() 使用 RelativeContainer 定位品牌 Logo(icon_start_logo,90×108)与应用名文案,并铺满 icon_start_bg 背景图;backgroundImageSize 设为 100%×100% 保证不同屏幕比例下无黑边。这与 module.json5 中透明启动窗口图标、纯色启动背景配合,形成"桌面图标 → 启动窗口 → 启动页 → 主页面"的平滑过渡。
主内容:启动初始化与用户偏好的装配
启动初始化横跨 EntryAbility.init() 与 LaunchPage,其数据源是 PreferencesHelper(偏好存储封装)与 Constant(常量定义),核心依赖链如下:
flowchart LR
subgraph sg_Prefs["PreferencesHelper(偏好存储)"]
LogSwitch["LOG_SAVING_SWITCH"]
AppSettings["APP_SETTINGS(AppOptions)"]
Agreement["user_agreement_status"]
end
subgraph sg_Consumers["消费者"]
EntryAbility["EntryAbility"]
LaunchPage["LaunchPage"]
end
subgraph sg_SDK["SDK 配置"]
BleConnect["BleConnectSettingConfigure.mtu"]
Communication["communicationWay: BLE / EDR"]
OtaWrapper["OTAWrapperOption 回调"]
end
LogSwitch -->|"onCreate 读取"| EntryAbility
AppSettings -->|"init() 读取"| EntryAbility
Agreement -->|"onPageShow 读取"| LaunchPage
EntryAbility --> BleConnect
EntryAbility --> Communication
EntryAbility --> OtaWrapper
LOG_SAVING_SWITCH:控制Log.startSaving()/Log.stopSaving(),实现日志持久化的持久化记忆;APP_SETTINGS:携带AppOptions(mtu、communication、isNeedAuth、isCustomReconnect等),是 SDK 行为的用户侧配置源;user_agreement_status:协议合规状态,只被LaunchPage读写。
三把钥匙分别对应启动的三个阶段:进程启动时恢复日志能力、窗口创建时装配 SDK、首屏显示时把关合规,互不耦合。
Core Flow:完整启动时序
sequenceDiagram
participant OS as HarmonyOS 系统
participant EA as EntryAbility
participant WS as WindowStage
participant LP as LaunchPage
participant PH as PreferencesHelper
participant BM as bluetoothInstance
participant BO as bluetoothOTAManager
OS->>EA: 根据 mainElement 拉起 EntryAbility
activate EA
EA->>PH: setContext(this.context)
EA->>PH: getValue(LOG_SAVING_SWITCH)
PH-->>EA: true → Log.startSaving(context)
EA-->>OS: onCreate 完成
OS->>EA: onWindowStageCreate(windowStage)
EA->>WS: setWindowLayoutFullScreen(true)
par 并行初始化
EA->>EA: init()
EA->>PH: getValue(APP_SETTINGS) → AppOptions
PH-->>EA: AppOptions
EA->>BM: 设置 mtu(默认 512)/ communicationWay
EA->>BO: bluetoothOTAManager.init()
EA->>BO: 注入 isUseAuth / isInnerReconnect 回调
and 页面加载
EA->>WS: loadContent('pages/LaunchPage')
WS-->>LP: 渲染启动页
LP->>PH: getValue(user_agreement_status)
alt 已同意协议
LP->>LP: tryToEnterMainPage() 进入主页面
else 未同意 / 首次启动
LP->>LP: dialogController.open() 弹出隐私协议
LP-->>LP: 用户点击同意 → 写入偏好 → 进入主页面
end
end
deactivate EA
流程要点:
- Ability 创建是纯同步轻量操作:
onCreate中唯一的异步动作是日志开关读取,不阻塞系统拉起; - 窗口创建后采用"并行初始化"策略:
init()与loadContent()同时推进,页面渲染无需等待 SDK 装配完成;但bluetoothOTAManager.init()在loadContent之前被调用(代码顺序上先this.init()后loadContent),保证主页面出现时 OTA 管理器已就绪; - 协议闸门发生在页面显示回调:
onPageShow晚于aboutToAppear,确保协议字符串资源已加载、弹窗控制器已构建,且每次回显都会重新校验; - 用户同意后:写回偏好并跳转主页面;再次启动时直接命中
true分支,跳过弹窗。
Usage Examples
以下代码均提取自仓库实际源码。
示例 1:EntryAbility 启动生命周期(onCreate + onWindowStageCreate)
onCreate 绑定偏好上下文并恢复日志开关;onWindowStageCreate 设置全屏、执行初始化并加载启动页:
onCreate(_want: Want, _launchParam: AbilityConstant.LaunchParam): void {
Log.i(TAG, 'Ability onCreate');
PreferencesHelper.getInstance().setContext(this.context)
PreferencesHelper.getInstance().getValue(PreferenceKey.LOG_SAVING_SWITCH).then((value) => {
Log.i(TAG, `open Log Saving: ${value}`)
if (typeof value === 'boolean' && value) {
Log.startSaving(this.context)
}
})
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
Log.i(TAG, 'Ability onWindowStageCreate');
// 设置成全屏
windowStage.getMainWindow((err, windowClass) => {
windowClass.setWindowLayoutFullScreen(true)
})
this.init()
windowStage.loadContent('pages/LaunchPage', (err, data) => {
if (err.code) {
Log.e(TAG, 'Failed to load the content. Cause: ' + JSON.stringify(err) ?? '');
return;
}
Log.i(TAG, 'Succeeded in loading the content. Data:' + JSON.stringify(data) ?? '');
});
}
Source: EntryAbility.ets
示例 2:init() 中的 SDK 装配与策略注入
init() 将用户偏好应用到蓝牙连接配置与 OTA 管理器,并通过闭包注入决策回调:
init() {
PreferencesHelper.getInstance().getValue(PreferenceKey.APP_SETTINGS).then((options) => {
const appOptions = options as (AppOptions|undefined)
const bleConnectSettingConfigure =
bluetoothInstance.bleImpl.getConnectSettingConfigure() as BleConnectSettingConfigure
bleConnectSettingConfigure.mtu = appOptions?.mtu ??512
bluetoothInstance.bleImpl.setConnectSettingConfigure(bleConnectSettingConfigure)
bluetoothInstance.communicationWay = appOptions?.communication == CommunicationType.SPP ?"EDR": "BLE"
bluetoothOTAManager.init()
const otaWrapperOption = bluetoothOTAManager.getOTAWrapper()?.getOTAWrapperOption()
if (otaWrapperOption) {
otaWrapperOption.isUseAuth = () => {
return appOptions?.isNeedAuth ?? true
}
otaWrapperOption.isInnerReconnect = () => {
return !appOptions?.isCustomReconnect ?? false
}
bluetoothOTAManager.getOTAWrapper()?.setOTAWrapperOption(otaWrapperOption)
}
})
}
Source: EntryAbility.ets
示例 3:LaunchPage 协议闸门与弹窗
onPageShow 依据协议状态分流,CustomDialogController 使用 autoCancel: false 强制用户决策:
onPageShow() {
PreferencesHelper.getInstance().getValue(KEY_USER_AGREEMENT).then((result) => {
if (result) {
let status = result as boolean
if (status) {
this.tryToEnterMainPage()
} else {
this.dialogController.open()
}
} else {
this.dialogController.open()
}
})
}
Source: LaunchPage.ets
private dialogController: CustomDialogController = new CustomDialogController({
cornerRadius: 12,
autoCancel: false,
onWillDismiss: () => {},
builder: CustomContentDialog({
contentBuilder: () => {
this.buildPrivacyContent();
},
}),
})
Source: LaunchPage.ets
示例 4:module.json5 入口声明
mainElement、pages、skills 与权限构成启动链路的外部契约:
{
"module": {
"name": "entry",
"type": "entry",
"mainElement": "EntryAbility",
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"startWindowIcon": "$media:ic_transparent",
"startWindowBackground": "$color:start_window_background",
"removeMissionAfterTerminate": true,
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"requestPermissions": [
{ "name": "ohos.permission.ACCESS_BLUETOOTH" },
{ "name": "ohos.permission.DISCOVER_BLUETOOTH" },
{ "name": "ohos.permission.USE_BLUETOOTH" },
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.GET_WIFI_INFO" }
]
}
}
Source: module.json5
Configuration Options
module.json5 关键配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
module.name | string | "entry" | 模块名,入口模块固定 |
module.mainElement | string | "EntryAbility" | 应用启动时默认拉起的 Ability |
module.pages | string | "$profile:main_pages" | 页面路由表配置引用 |
module.deviceTypes | string[] | ["phone","tablet"] | 支持的设备类型 |
module.abilities[0].srcEntry | string | "./ets/entryability/EntryAbility.ets" | Ability 实现入口 |
module.abilities[0].startWindowIcon | string | "$media:ic_transparent" | 启动窗口图标(透明) |
module.abilities[0].startWindowBackground | string | "$color:start_window_background" | 启动窗口背景色 |
module.abilities[0].removeMissionAfterTerminate | bool | true | 终止后移除任务 |
module.abilities[0].skills | object[] | home 意图 | 桌面入口声明 |
偏好存储键(PreferencesHelper)
| 键 | 类型 | 默认行为 | 读取方 | 说明 |
|---|---|---|---|---|
LOG_SAVING_SWITCH | boolean | 未设置时不落盘 | EntryAbility | 日志落盘开关 |
APP_SETTINGS | AppOptions | 未设置时用默认值 | EntryAbility.init() | 应用级 SDK 设置 |
user_agreement_status | boolean | 未设置视为未同意 | LaunchPage | 用户协议同意状态 |
AppOptions 字段(启动时应用到 SDK)
| 字段 | 类型 | 默认值 | 生效位置 |
|---|---|---|---|
mtu | number | 512 | BleConnectSettingConfigure.mtu |
communication | CommunicationType | — | communicationWay:SPP → "EDR",其余 → "BLE" |
isNeedAuth | boolean | true | OTAWrapperOption.isUseAuth 回调返回值 |
isCustomReconnect | boolean | false | OTAWrapperOption.isInnerReconnect 回调返回值(取反) |
isHid | boolean | — | 扫描配置 isContainSystemsConnectedDevice(源码中处于注释状态) |
API Reference
EntryAbility.onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void
Ability 创建回调。绑定 PreferencesHelper 上下文,异步恢复日志落盘开关。
参数:
want(Want):拉起 Ability 的意图信息launchParam(AbilityConstant.LaunchParam):启动原因与参数
返回: 无
EntryAbility.onWindowStageCreate(windowStage: window.WindowStage): void
主窗口创建回调。设置全屏、调用 init()、加载 pages/LaunchPage。
参数:
windowStage(window.WindowStage):窗口阶段对象,用于getMainWindow与loadContent
返回: 无
EntryAbility.init(): void
读取 APP_SETTINGS 偏好,将 MTU、通信方式写入 bluetoothInstance,并初始化 bluetoothOTAManager、注入 isUseAuth/isInnerReconnect 策略回调。
返回: 无
EntryAbility.onWindowStageDestroy(): void
窗口销毁回调。根据 LOG_SAVING_SWITCH 异步停止日志落盘。
EntryAbility.onForeground(): void / onBackground(): void
前后台切换回调,仅记录日志。
LaunchPage.aboutToAppear(): void
组件即将出现时同步读取协议文案字符串资源。
LaunchPage.onPageShow(): void
页面显示回调。读取 user_agreement_status:true → tryToEnterMainPage();否则打开协议弹窗。
LaunchPage.buildPrivacyContent(): void(@Builder)
构建隐私协议弹窗内容,含协议标题、声明文案与两个可点击协议链接。
失败模式、边界情况与并发
页面加载失败
loadContent('pages/LaunchPage', callback) 的回调中显式检查 err.code:非 0 时记录 Failed to load the content 错误日志并提前返回。注意此处 Log.e 的第二个参数是 JSON.stringify(err) ?? '' —— 由于 ?? 前是字符串拼接结果(不可能为 null/undefined),该空值合并实际不生效,若 err 为 null 会输出 "null" 字符串,属于可改进的防御性写法。
首次启动 vs 再次启动
PreferencesHelper.getValue(KEY_USER_AGREEMENT) 的 then 回调中,偏好不存在时 result 为 undefined,落入 else 分支打开弹窗。这保证:
- 首次安装启动(无任何偏好记录)→ 强制弹窗;
- 用户曾同意(
true)→ 直接进入主页面; - 用户曾拒绝(
false)→ 再次弹窗(拒绝不会写入"永久拒绝"状态,重新启动仍会询问)。
偏好读取的异步性
PreferencesHelper 的 getValue 全部返回 Promise。这意味着 EntryAbility.init() 中的 SDK 装配发生在微任务阶段,晚于同步的 loadContent 调用(但 loadContent 本身也是异步渲染)。若主页面在极短时间内访问尚未配置的 OTA 管理器,理论上存在窗口期;实践中 bluetoothOTAManager.init() 的调用顺序在 loadContent 之前发起,且 SDK 内部对未初始化状态有兜底,实际影响有限。
日志开关的启停对称性
onCreate 启动 Log.startSaving,onWindowStageDestroy 停止 Log.stopSaving,且两者都先读取偏好再决定动作,保证开关状态与用户设置一致。若 onCreate 的异步读取尚未完成时窗口即被销毁(极端快速退出),onWindowStageDestroy 不会误停未启动的保存流程(同样受偏好开关约束)。
弹窗强制性与返回键处理
autoCancel: false 阻止点击遮罩关闭,onWillDismiss: () => {} 空实现拦截系统默认关闭,保证用户必须通过"同意/不同意"按钮显式表态,满足隐私合规的"主动确认"要求。代价是:用户若既不点同意也不点不同意,将停留在启动页,应用无法进入主流程 —— 这是合规优先于体验的设计取舍。
性能与运维考量
- 启动路径无同步 IO:
onCreate与init()中所有偏好读取均为异步 Promise,不阻塞 UI 线程;LaunchPage.aboutToAppear中的getStringSync仅访问内存中的资源表,开销可忽略。 - 全屏渲染:
setWindowLayoutFullScreen(true)让启动页使用完整屏幕,配合backgroundImageSize100%×100% 避免不同屏幕比例下的黑边/留白。 - 并行初始化:SDK 装配与首屏渲染并行推进,缩短"点击图标 → 看到页面"的感知时间。
- 日志落盘按需开启:只有用户显式开启
LOG_SAVING_SWITCH才启动文件写入,避免默认全量落盘对存储与性能的影响;问题排查时可通过设置页开启。
扩展点
- OTA 策略回调注入(
OTAWrapperOption.isUseAuth/isInnerReconnect):SDK 在升级流程中通过闭包询问上层,接入方无需修改 SDK 即可动态控制"是否需要鉴权"与"是否使用 SDK 内置重连"。当前实现把二者绑定到AppOptions偏好,未来可替换为云端策略或运行时状态。 - 通信方式切换(
communicationWay: "BLE" | "EDR"):init()中根据AppOptions.communication一行切换,新增通信类型只需扩展CommunicationType枚举与映射逻辑。 - 启动页协议文案:协议标题、声明与链接全部资源化(
$r('app.string.*')+AgreementUrl常量),支持多语言与换版,无需改动代码结构。 - MTU 配置:
BleConnectSettingConfigure.mtu支持按设备/场景调整(默认 512),是调优传输吞吐的入口。
测试
仓库中未发现针对入口与启动流程的独立测试文件(未检索到 EntryAbility/LaunchPage 的测试用例)。启动链路的正确性目前依赖真机运行验证,包括:首装弹窗、协议同意后直达主页面、日志开关持久化、全屏布局在不同分辨率下的表现。建议后续补充 UI 自动化用例覆盖协议闸门的三种分支(同意/拒绝/首次启动)。
Related Links
- EntryAbility.ets —— 入口 Ability 生命周期与启动初始化
- LaunchPage.ets —— 启动页与隐私协议闸门
- module.json5 —— 模块入口声明与权限配置
- 相关兄弟主题:蓝牙 OTA 管理器(
BluetoothOTAManager)、BLE/SPP 通信实现(bluetooth/ble、bluetooth/spp)、偏好存储封装(common/PreferencesHelper)、日志模块(tool/log/Log)—— 详见各自目录页面