基于鸿蒙os开发跑步管理系统(十四)-问题清单与已知限制
问题清单与已知限制
概述
本文档记录 RunAdvisor 应用开发过程中发现的已知问题、已修复的 Bug 及当前仍存在的限制。每个问题包含问题描述、影响范围、修复状态和临时解决方案。
已修复问题
[FIXED] Canvas 时序 Bug - aboutToAppear 读取 width/height 为 0
问题编号: BUG-001
严重程度: 高
描述: Canvas 组件的 aboutToAppear 生命周期回调在 onReady 之前执行。若在 aboutToAppear 中读取 canvasContext.width 或 canvasContext.height,将得到 0 值,导致 drawRouteMap() 中所有坐标计算失败,地图无法渲染。
影响范围: 路线地图页面,所有三条路线的地图均无法显示。
根本原因: ArkUI 的 Canvas 组件在 aboutToAppear 阶段尚未完成布局测量,此时 width/height 属性值为默认的 0。
修复方案: 将地图绘制触发时机从 aboutToAppear 移至 Canvas 的 onReady 回调,并在 drawRouteMap() 中实时读取 ctx.width/ctx.height:
Canvas(this.mapCanvas)
.width('100%')
.height(320)
.onReady(() => {
this.mapRouteType = routeType;
this.currentMapRoute = RouteService.getInstance().getRouteByType(routeType);
this.drawRouteMap();
})
private drawRouteMap(): void {
const ctx: CanvasRenderingContext2D = this.mapCanvas;
const canvasW: number = ctx.width;
const canvasH: number = ctx.height;
if (canvasW <= 0 || canvasH <= 0) {
return;
}
// ... 绘制逻辑
}
修复状态: 已修复
验证方法: 进入路线详情页面,确认地图正常渲染,路线折线和地标标注可见。
未修复问题
[UNFIXED] 天气 API Key 为占位符
问题编号: BUG-002
严重程度: 中
描述: Constants.WEATHER_API_KEY 的值为占位字符串 'YOUR_KEY',无法调用 OpenWeatherMap API 获取真实天气数据。
static readonly WEATHER_API_KEY: string = 'YOUR_KEY';
影响范围: 天气功能完全不可用,首页天气卡片始终显示默认天气数据。
降级行为: WeatherService 在请求失败后自动返回默认天气数据:
getDefaultWeather(): WeatherInfo {
return WeatherInfo.create(22, 20, 55, 3, '晴', '01d', '北京');
}
临时方案: 用户需替换 Constants.WEATHER_API_KEY 为有效的 OpenWeatherMap API Key。
修复建议:
- 申请有效的 OpenWeatherMap API Key 并替换
- 或搭建后端代理服务隐藏 API Key
- 或切换到免费天气 API(如和风天气免费版)
[UNFIXED] StepService 传感器在模拟器上返回 0
问题编号: BUG-003
严重程度: 低
描述: sensor.once(sensor.SensorId.PEDOMETER) 在模拟器上调用后可能返回步数为 0,因为模拟器没有物理计步器传感器。
private querySensorSteps(): Promise<number> {
return new Promise((resolve: (value: number) => void) => {
try {
sensor.once(sensor.SensorId.PEDOMETER, (data: sensor.PedometerResponse) => {
resolve(data.steps);
});
} catch (e) {
resolve(0);
}
setTimeout(() => { resolve(0); }, 3000);
});
}
影响范围: 模拟器上首页步数始终显示 0。真机上功能正常。
降级行为: StepService 优先读取 Preferences 中的缓存步数,仅缓存为 0 时才查询传感器:
async getStepCount(): Promise<number> {
const prefs: PreferencesService = PreferencesService.getInstance();
const saved: number = prefs.getStepCount();
if (saved > 0) {
this.currentSteps = saved;
return saved;
}
const steps: number = await this.querySensorSteps();
// ...
}
临时方案: 在模拟器上测试时,可通过手动调用 StepService.setSteps() 模拟步数数据。
[UNFIXED] 陀螺仪权限在模拟器上不可用
问题编号: BUG-004
严重程度: 低
描述: ohos.permission.GYROSCOPE 权限在模拟器上可能无法授予,导致 sensor.on(sensor.SensorId.GYROSCOPE) 调用抛出异常。
startGyroscope(): void {
try {
sensor.on(sensor.SensorId.GYROSCOPE, (data: sensor.GyroscopeResponse) => {
this.lastGyroX = data.x;
this.lastGyroY = data.y;
this.gyroAvailable = true;
}, { interval: 100000000 });
this.listening = true;
} catch (e) {
this.gyroAvailable = false;
}
}
影响范围: 模拟器上卡尔曼滤波位置增强功能不可用,enhance() 直接返回原始 GPS 坐标。真机上功能正常。
降级行为: gyroAvailable = false 时,enhance() 方法跳过卡尔曼滤波计算:
if (!this.gyroAvailable) {
this.enhancedLat = rawLat;
this.enhancedLng = rawLng;
return [rawLat, rawLng];
}
临时方案: 无需处理,降级后应用其他功能正常。
[UNFIXED] 签名配置未完成
问题编号: BUG-005
严重程度: 高(仅影响真机部署)
描述: 项目未配置 HarmonyOS 应用签名,无法在真机上安装和运行应用。
影响范围: 无法将应用推送到真机设备,仅能在模拟器上运行。
临时方案: 在 DevEco Studio 中手动配置签名:
- 打开 File > Project Structure > Project > Signing Configs
- 勾选 Automatically generate signature
- 或手动指定证书和 Profile 文件
[UNFIXED] ‘app_name’ 字符串资源冲突警告
问题编号: BUG-006
严重程度: 低
描述: 编译时可能出现 app_name 字符串资源与 HarmonyOS 系统资源名称冲突的警告。
影响范围: 仅编译警告,不影响运行。
临时方案: 将 app_name 重命名为项目特定的名称,如 run_advisor_app_name。
[UNFIXED] WeatherService 缓存逻辑错误
问题编号: BUG-007
严重程度: 中
描述: WeatherService 的缓存读写逻辑存在错误,cacheWeather() 和 getCachedWeather() 使用了错误的 Preferences 键(调用了 saveStepCount/getStepCount 而非专用的天气缓存键)。
private getCachedWeather(): WeatherInfo | undefined {
try {
const prefs: PreferencesService = PreferencesService.getInstance();
const cacheTime: number = prefs.getStepCount(); // 错误: 使用了步数键
// ...
} catch (e) {}
return undefined;
}
private cacheWeather(info: WeatherInfo): void {
try {
const prefs: PreferencesService = PreferencesService.getInstance();
prefs.saveStepCount(0); // 错误: 覆盖了步数数据
} catch (e) {}
}
影响范围:
- 天气缓存永远无法命中,每次都重新请求
- 缓存写入会清零步数数据
临时方案: 由于 API Key 为占位符,天气请求始终失败并返回默认数据,此 Bug 的实际影响被掩盖。修复 API Key 后需同时修复此缓存逻辑。
修复建议: 使用 WEATHER_CACHE_KEY 和 WEATHER_CACHE_TIME_KEY 专用键存储天气缓存数据,并序列化 WeatherInfo 对象。
已知限制
模拟器功能限制
| 功能 | 模拟器状态 | 说明 |
|---|---|---|
| 天气数据 | 降级 | API Key 未配置,使用默认数据 |
| 步数统计 | 降级 | 传感器返回0,使用缓存值 |
| 陀螺仪增强 | 不可用 | 传感器不可用,直接使用GPS原始值 |
| GPS定位 | 降级 | 返回北京默认坐标 |
| 地图渲染 | 正常 | Canvas渲染不依赖传感器 |
| 饮食记录 | 正常 | 本地存储,无外部依赖 |
| 跑步建议 | 降级 | 基于默认天气数据计算 |
功能限制
- 路线数据固定: 三条路线硬编码在 RouteService 中,无法动态加载或用户自定义
- 地标标注固定: 9个北京地标硬编码,不随城市变化
- 食物数据库固定: CalorieService 中的90种食物硬编码,无法扩展
- 无后台追踪: 应用不支持后台持续位置追踪和跑步记录
- 无社交功能: 不支持跑步数据分享和好友互动
- 单用户模式: 不支持多用户切换,所有数据存储在同一 Preferences 实例中
UI 限制
- 无深色模式: 当前仅支持浅色主题,未适配深色模式
- 无国际化: 界面文字全部硬编码为中文,不支持多语言
- 无平板适配: 界面布局针对手机屏幕设计,平板上未做特殊适配
- 无手势操作: 地图不支持拖拽、缩放手势
数据限制
- 无云端同步: 所有数据存储在本地 Preferences,卸载应用即丢失
- 无历史数据: 步数和饮食记录仅保留当天数据
- 天气缓存失效: 由于缓存逻辑 Bug,天气数据无法有效缓存
问题修复优先级建议
| 优先级 | 问题 | 理由 |
|---|---|---|
| P0 | BUG-005 签名配置 | 阻塞真机测试 |
| P1 | BUG-002 API Key | 核心功能不可用 |
| P1 | BUG-007 缓存逻辑 | 数据正确性受影响 |
| P2 | BUG-003 模拟器步数 | 影响开发测试体验 |
| P2 | BUG-004 模拟器陀螺仪 | 影响开发测试体验 |
| P3 | BUG-006 资源命名 | 仅编译警告 |
测试覆盖缺口
- 无自动化测试框架,所有测试为手动执行
- 无 UI 自动化测试
- 无传感器模拟测试
- 无网络异常模拟测试
- 无并发数据访问测试
已修复问题补充
[FIXED] FoodInputDialog 布局 Bug - position + zIndex 导致遮罩异常
问题编号: BUG-008
严重程度: 中
描述: FoodInputDialog(饮食记录输入弹窗)使用 position({x:0, y:0}) + zIndex(999) 实现全屏遮罩效果,但在某些布局容器中 position 定位参考点不确定,导致遮罩层偏移或无法覆盖整个屏幕。
// 问题代码
Column() {
// ... 弹窗内容
}
.position({ x: 0, y: 0 })
.zIndex(999)
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
根本原因: position() 的定位参考点为父容器的左上角,当父容器不是全屏时,position({x:0, y:0}) 仅覆盖父容器区域。在 Tabs + TabContent 的嵌套结构中,父容器高度可能不等于屏幕高度。
修复方案: 使用 Stack 容器替代 position + zIndex,Stack 的子组件自动层叠:
Stack() {
// 原有页面内容
if (this.showFoodDialog) {
Column() {
// 弹窗内容
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
}
}
Stack 方案的优势:子组件按声明顺序从底到顶层叠,无需手动管理 zIndex;覆盖范围始终为 Stack 的完整区域,不受父容器布局影响。
修复状态: 已修复
[FIXED] build() 中 const 声明导致 ArkTS 编译错误
问题编号: BUG-009
严重程度: 高
描述: 在 build() 方法内使用 const 声明局部变量时,ArkTS 编译器报错 “Only UI component syntax is allowed in build”。
// 问题代码
build() {
const bmi: number = this.getBMI(); // 编译错误
const category: string = this.getBMICategory(); // 编译错误
Column() { ... }
}
根本原因: ArkTS 严格模式对 build() 方法有特殊限制,仅允许 UI 组件语法,不允许声明 const/let 变量。这是 ArkTS 与标准 TypeScript 的重要差异之一。
修复方案: 将计算逻辑移到方法外部,使用 @State 或 get 方法替代:
// 方案一:使用 get 方法
private getBMI(): number {
const heightM: number = this.profile.height / 100;
return heightM > 0 ? this.profile.weight / (heightM * heightM) : 0;
}
// 在 build() 中调用
Text(this.getBMI().toFixed(1))
// 方案二:使用 @State 缓存(当值需要在多处使用时)
@State bmiValue: number = 0;
aboutToAppear() {
this.bmiValue = this.profile.weight / ((this.profile.height / 100) ** 2);
}
修复状态: 已修复
[FIXED] RunAdvice 模型缺少 description 字段
问题编号: BUG-010
严重程度: 中
描述: RunAdvice 数据模型最初只有 title 和 type 字段,缺少 description 字段,导致跑步建议页面无法显示建议的详细说明文字。
// 问题代码
class RunAdvice {
title: string = '';
type: string = '';
// 缺少 description 字段
}
修复方案: 增加 description 字段并在建议生成逻辑中填充内容:
class RunAdvice {
title: string = '';
type: string = '';
description: string = '';
static create(title: string, type: string, description: string): RunAdvice {
const advice: RunAdvice = new RunAdvice();
advice.title = title;
advice.type = type;
advice.description = description;
return advice;
}
}
修复状态: 已修复
[FIXED] geoLocationManager.CurrentLocationRequest 构造方式错误
问题编号: BUG-011
严重程度: 中
描述: LocationService 中使用 new geoLocationManager.CurrentLocationRequest() 构造定位请求对象,但该 API 不支持 new 构造函数调用。
// 问题代码
const requestInfo = new geoLocationManager.CurrentLocationRequest();
修复方案: 改用对象字面量方式构造:
const requestInfo: geoLocationManager.CurrentLocationRequest = {
priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
};
ArkTS 严格模式下,许多系统 API 的类型只支持对象字面量初始化,不支持 new 构造。这是 ArkTS 与 TypeScript 的又一重要差异。
修复状态: 已修复
[FIXED] WeatherService 缓存键名错误
问题编号: BUG-007 (已修复版本)
严重程度: 中
描述: 此问题在"未修复问题"部分已记录,但实际上已在后续开发中修复。修复内容为使用专用天气缓存键替代步数键。
修复方案:
// 修复前
private getCachedWeather(): WeatherInfo | undefined {
const cacheTime: number = prefs.getStepCount(); // 错误
// ...
}
// 修复后
private getCachedWeather(): WeatherInfo | undefined {
const cacheTime: number = prefs.getWeatherCacheTime(); // 正确
const cacheJson: string = prefs.getWeatherCache(); // 正确
if (cacheJson && cacheTime > 0) {
// 反序列化天气数据
return WeatherInfo.fromJSON(cacheJson);
}
return undefined;
}
private cacheWeather(info: WeatherInfo): void {
prefs.saveWeatherCache(JSON.stringify(info));
prefs.saveWeatherCacheTime(Date.now());
}
PreferencesService 中增加了专用方法:
getWeatherCache(): string {
return this.ensureStore().getSync(Constants.WEATHER_CACHE_KEY, '') as string;
}
saveWeatherCache(json: string): void {
this.ensureStore().putSync(Constants.WEATHER_CACHE_KEY, json);
this.ensureStore().flushSync();
}
getWeatherCacheTime(): number {
return this.ensureStore().getSync(Constants.WEATHER_CACHE_TIME_KEY, 0) as number;
}
saveWeatherCacheTime(time: number): void {
this.ensureStore().putSync(Constants.WEATHER_CACHE_TIME_KEY, time);
this.ensureStore().flushSync();
}
修复状态: 已修复
架构级限制
单文件架构
所有业务逻辑和 UI 代码集中在 Index.ets 一个文件中(约 1300+ 行),带来以下问题:
- 可维护性差: 修改某个功能需要在 1300 行中定位相关代码
- 编译效率低: 修改任何部分都需要重新编译整个文件
- 协作困难: 多人无法同时编辑同一文件
- 复用困难: 服务和组件无法跨页面复用
理想架构应将代码拆分为:
- services/ 目录:各 Service 类独立文件
- models/ 目录:UserProfile, RouteInfo, WeatherInfo 等数据模型
- pages/ 目录:各 Tab 页面和 NavDestination 页面
- components/ 目录:可复用的 UI 组件
当前单文件架构的原因是开发初期的快速迭代需求,后续版本应逐步重构拆分。
无数据库支持
当前所有数据通过 Preferences (键值对) 存储,适用于简单场景,但存在以下限制:
- 查询能力弱: 无法按条件查询数据(如查询某日期的饮食记录)
- 关系数据不支持: 无法建立数据间的关联关系
- 大数据量性能差: 饮食记录增长后,序列化/反序列化开销增大
- 无事务支持: 多个键的写入不是原子操作
HarmonyOS 提供了关系型数据库 (@kit.ArkData) 和分布式数据服务,后续版本可引入用于存储历史记录和饮食日志。
无后台服务
应用不支持后台持续运行,导致以下功能缺失:
- 实时跑步追踪: 无法在后台记录 GPS 轨迹和跑步数据
- 自动计步: 应用退到后台后无法持续计步
- 定时提醒: 无法在后台定时推送运动提醒
- 数据同步: 无法在后台自动同步数据
实现后台服务需要使用 BackgroundTaskManager 和 ContinuousTask,同时需要声明 KEEP_BACKGROUND_RUNNING 权限。
开发环境限制
DevEco Studio 版本依赖
RunAdvisor 基于 HarmonyOS 6.1.1 (API 24) 开发,DevEco Studio 版本需匹配:
| 组件 | 最低版本 | 当前使用版本 |
|---|---|---|
| DevEco Studio | 5.0.3 | 5.0.3+ |
| HarmonyOS SDK | API 24 | API 24 |
| ArkTS | 严格模式 | 严格模式 |
| hvigor | 4.0 | 4.0+ |
构建警告
当前构建产生两个警告:
- GYROSCOPE 权限警告: 提醒 user_grant 权限需运行时请求,已在代码中处理
- 签名跳过警告: Will skip sign ‘hos_hap’,需配置签名后消除
两个警告均不影响模拟器调试,但在真机部署前需解决签名问题。
模拟器能力矩阵
| 功能 | 模拟器 | 真机 | 差异说明 |
|---|---|---|---|
| 天气查询 | 降级(默认数据) | 正常(API Key配置后) | 网络请求可用但Key为占位符 |
| GPS定位 | 降级(北京默认) | 正常 | 模拟器无GPS硬件 |
| 计步器 | 不可用(返回0) | 正常 | 模拟器无计步传感器 |
| 陀螺仪 | 不可用 | 正常 | 模拟器无陀螺仪 |
| 地图渲染 | 正常 | 正常 | Canvas不依赖硬件 |
| 饮食记录 | 正常 | 正常 | 纯本地功能 |
| 路线推荐 | 正常 | 正常 | 硬编码数据 |
| 资料设置 | 正常 | 正常 | 纯本地功能 |
问题复现步骤
BUG-002 天气 API Key 占位符
- 启动应用,进入首页
- 观察天气卡片区域
- 预期:显示当前真实天气;实际:始终显示"北京 22°C 晴"
- 检查 hilog 输出,可见天气 API 请求失败日志
BUG-003 模拟器步数为0
- 在模拟器上启动应用
- 进入首页,观察步数显示
- 预期:显示非零步数;实际:显示 0
- 真机上测试可见正常步数
BUG-004 模拟器陀螺仪不可用
- 在模拟器上启动应用
- 检查 hilog 输出
- 可见 “LocationEnhanceService: gyroscope not available” 警告
- enhance() 直接返回 GPS 原始坐标,无滤波效果
BUG-005 签名配置缺失
- 执行 build_project --product default
- 构建输出中出现 “Will skip sign ‘hos_hap’” 警告
- 尝试 start_app 推送到真机
- 安装失败,提示签名错误
BUG-008 FoodInputDialog 布局 (已修复)
- 进入饮食页面
- 点击添加食物按钮
- 弹窗应覆盖整个屏幕并显示半透明遮罩
- 修复前:遮罩可能偏移或尺寸异常;修复后:正常显示
BUG-009 build() const 声明 (已修复)
- 在 build() 方法内添加 const 声明
- 执行构建
- 编译错误:“Only UI component syntax is allowed in build”
- 将 const 移到方法外部后编译通过
问题修复历史
| 日期 | 问题 | 状态 |
|---|---|---|
| 初始 | BUG-001 Canvas时序 | 已修复 |
| 初始 | BUG-008 FoodInputDialog布局 | 已修复 |
| 初始 | BUG-009 build() const | 已修复 |
| 初始 | BUG-010 RunAdvice缺字段 | 已修复 |
| 初始 | BUG-011 CurrentLocationRequest | 已修复 |
| 后续 | BUG-007 缓存键名 | 已修复 |
| 待定 | BUG-002 API Key | 未修复 |
| 待定 | BUG-003 模拟器步数 | 未修复(环境限制) |
| 待定 | BUG-004 模拟器陀螺仪 | 未修复(环境限制) |
| 待定 | BUG-005 签名配置 | 未修复(需手动配置) |
| 待定 | BUG-006 资源命名 | 未修复(仅警告) |
后续改进建议
短期 (v1.1)
- 配置有效的天气 API Key 或搭建代理服务
- 在 DevEco Studio 中配置签名,支持真机部署
- 将 Index.ets 拆分为多个文件,改善可维护性
- 为 app_name 资源重命名避免冲突警告
中期 (v2.0)
- 引入关系型数据库存储历史记录
- 实现后台跑步追踪服务
- 支持用户自定义路线
- 实现深色模式适配
- 支持国际化 (中英文)
长期 (v3.0)
- 实现云端数据同步
- 增加社交功能(跑步数据分享)
- 支持更多城市路线
- 集成健康设备数据(智能手表等)
- 实现自适应卡尔曼滤波参数
调试指南
hilog 日志查看
通过 hdc 工具查看应用运行日志:
hdc shell hilog | grep RunAdvisor
关键日志标识:
- PreferencesService: 数据存储操作日志
- LocationEnhanceService: 位置增强和陀螺仪日志
- WeatherService: 天气查询和缓存日志
- StepService: 步数查询日志
常见问题排查
| 症状 | 可能原因 | 排查方法 |
|---|---|---|
| 天气始终默认 | API Key 占位符 | 检查 Constants.WEATHER_API_KEY 值 |
| 步数为0 | 模拟器无传感器 | 在真机上测试或手动设置步数 |
| 地图空白 | Canvas 时序问题 | 确认 onReady 回调正常触发 |
| 弹窗位置偏移 | position 布局问题 | 确认已使用 Stack 方案 |
| 编译错误"Only UI" | build() 中有 const | 将变量移到方法外部 |
| 真机安装失败 | 未配置签名 | 在 DevEco Studio 中配置签名 |
| 位置增强无效 | 陀螺仪不可用 | 检查 hilog 中陀螺仪警告 |
更多推荐




所有评论(0)