说明文档及下载链接
一、数据库与数据持久化问题
1.1 破坏性迁移导致数据丢失
文件: InspectorDatabase.kt:273
严重度: 严重
.fallbackToDestructiveMigration()
数据库当前版本: v13
问题:
fallbackToDestructiveMigration()会在数据库版本升级时删除所有表并重建- 从 v1 到 v13 没有任何 Migration 路径
- 用户升级应用会丢失所有历史检测记录和坐标库数据
- 对于检测现场来说,数据丢失是灾难性的
修改:
- 上架前必须为
version = 13之前的每个版本编写 Migration 代码 - 或在首个上架版本设置
version = 1,清除开发阶段的版本历史 - 设置
exportSchema = true并配置room.schemaLocation以追踪架构变更
1.2 DAO 查询冗余和性能隐患
文件: InspectorDatabase.kt:109-165
严重度: 中
存在大量功能重叠的 DAO 查询:
findNearestPoints,findNearestTwoPoints,findNearestPointsBounded(x2),findNearestPointsAll,findNearestTwoPointsAll,findNearestPointsAllBounded,findNearestTwoPointsBounded
问题:
- 8 个类似的查询方法,维护成本高
- 无空间索引优化(虽然加了
INDEX但只在(latitude, longitude)上,欧氏距离排序无法利用索引) ORDER BY ((lat-:lat)² + (lng-:lng)²)在大表上性能极差
修改:
- 保留 2-3 个核心查询方法即可
- 使用 R-tree 空间索引(Room 支持
@Fts4)
1.3 Transactions 未充分使用
文件多处: InspectorDatabase.kt, MainActivity.kt:1854
问题:
records.chunked(500).forEach { batch -> database?.detectionDao()?.insertAll(batch) }逐批插入但未包在事务中- 批量插入时如果没有事务包装,每个
insertAll可能开启独立的事务
修改:
- 大数据量写入时使用
@Transaction包装
二、架构与代码质量
2.1 MainActivity.kt 4002 行 — 严重违反单一职责
严重度: 严重
这个文件包含了:
MainActivity(Activity 生命周期)InspectorViewModel(View Model + 业务逻辑)InspectorState(数据类)Screen(导航)Trans(多语言词典)- 全部 10+ 个 Composable 界面函数
- 自定义图表、仪表盘等 UI 组件
修改:
- 将
Trans对象拆分为独立文件Translations.kt - 将
InspectorViewModel拆分为独立文件 - 将 Composable 界面按功能拆分到多个文件
- 每个文件控制在 400 行以内
2.2 传感器数据处理逻辑双重实现
文件:
InspectorService.kt:542-671(Service 中的onSensorChanged)MainActivity.kt:1159-1267(ViewModel 中的onSensorChanged)
严重度: 高
问题:
- Service 和 ViewModel 各自有几乎完全相同的传感器数据处理逻辑(重力对齐、滤波、峰值检测、超限计算)
- 两处逻辑使用不同的参数(
gravityAlpha不同:Service 用 0.015,ViewModel 用 0.0005) - 两处逻辑可能同时注册传感器监听,导致冲突
建议:
- 传感器数据仅在 Service 中处理
- ViewModel 通过
StateFlow观察 Service 的状态 - 将传感器算法抽取为独立工具类
2.3 双重 GPS 订阅
文件:
InspectorService.kt:283(Service 的fusedLocationClient.requestLocationUpdates)MainActivity.kt:1149(ViewModel 的fusedLocationClient?.requestLocationUpdates)
严重度: 高
问题:
- Service 和 ViewModel 同时订阅位置更新
- 虽然
observeService中调用了stopLocationUpdates(),但流程并非完全可靠 - 两个 location callback 都会查询数据库,导致不必要的 I/O
建议:
- GPS 位置更新仅在 Service 中处理
- ViewModel 不应独立订阅位置
2.4 未使用枚举/密封类表示状态
多处:
问题:
- 超限等级使用
Int(0,1,2,3) - 超限类型使用
String("NORMAL", "H1", "V2", "H3 V3") - 行别使用
String("上行", "下行", "单线")
建议:
- 使用
sealed class或enum class提高类型安全性
三、计算逻辑缺陷
3.1 水平加速度方向判定不精确
文件: InspectorService.kt:589 和 MainActivity.kt:1210
val hAccSigned = if (lx < 0) -hAccMagnitude else hAccMagnitude
问题:
- 仅用 x 轴的符号来决定水平加速度方向
- 当手机在车上非标准安装时,x 轴与轨道方向不对齐
- 导致水平超限的方向判断不准确
建议:
- 应基于重力方向投影完整计算水平矢量方向
- 或使用航向角传感器进行轴对齐
3.2 死区阈值过于精细
文件: InspectorService.kt:604-605
val finalV = if (abs(avgV) < 0.008f) 0f else avgV
val finalH = if (abs(avgH) < 0.012f) 0f else avgH
问题:
- 0.008G 和 0.012G 的死区阈值非常小(接近传感器噪声水平)
- 可能导致频繁的 0 值与微小值切换,造成波形毛刺
建议:
- 提高死区阈值至 0.02-0.03G 范围
- 或使用自适应死区(基于当前噪声水平)
3.3 KalmanFilter 参数固定
文件: InspectorService.kt:98-107
private class KalmanFilter(private val processNoise: Float, private val measurementNoise: Float)
实例化: speedKalman = KalmanFilter(0.1f, 1.0f)
问题:
measurementNoise固定为 1.0f,不随 GPS 精度变化- 当 GPS 精度 3m 时与精度 50m 时使用相同权重,不合理
建议:
- 根据
loc.accuracy动态调整测量噪声协方差
3.4 里程推算的边界条件
文件: InspectorService.kt:350-387
val dtSec = (dtMillis / 1000.0).coerceAtMost(5.0)
// ...
val displacementMeters = currentSpeed * dtSec
问题:
dtSec被限制在 5 秒内,但MAX_DEAD_RECKONING_MS可达 30 分钟- 当 GPS 丢失超过 5 秒时,
dtSec被钳制到 5 秒,但lastValidTime也在每次迭代后被更新 - 这意味着每秒增加
currentSpeed * 5米的位移 → 里程估算被放大 5 倍
分析:
- 每次循环
lastValidTime = currentTime(行 385) - 下一轮
dtMillis = 1000(因为循环每秒执行一次) dtSec = min(1000/1000, 5) = 1- 所以实际上
dtSec每次都是 1 秒 - 但如果
lastValidTime没有被更新(即链路异常),会出现 dtSec 被钳制到 5 的情况
建议: 这个逻辑整体上看是正确的,但需要增加防御性检查和单元测试
四、性能问题
4.1 波形图全量加载
文件: MainActivity.kt:2921
records = viewModel.getSessionRecords(sessionId)
问题:
- 一次加载全部记录(可能上万条)
- 在 UI 线程处理列表操作
4.2 数据库查询频繁
每个 GPS 定位都会触发:
findNearestPoints(5条SQL)findNearestPointsAll(5条SQL)getFeaturesNearMileage(1条SQL)
当 GPS 以 500ms-1000ms 间隔更新时,数据库 I/O 压力大
4.3 坐标库全量加载
getAllMileageRefs()用于导出- 锚点遍历使用所有锚点
4.4 导出时的内存使用
- 导出 CSV 时构建
StringBuilder可能会占用大量内存
五、测试覆盖
5.1 仅有默认测试文件
ExampleUnitTest.kt- 空测试ExampleInstrumentedTest.kt- 空测试- 零实际的单元测试和集成测试
5.2 关键逻辑无测试覆盖
- 里程计算
MileageUtils.calculateMileageValue - 位移计算
calculateDisplacedMileage - 传感器滤波算法
- GPS 丢失后的推算逻辑
- 授权码的签名验证
修改:
- 为
MileageUtils编写单元测试 - 为授权验证逻辑编写测试
- 为数据库 DAO 编写 Android 测试
暂无评论