# 罗盘问题排查记录
> 文档版本：v1.0
> 最后更新：2026-04-02 08:28:05


## 背景

本项目在微信小程序中使用罗盘驱动：

- 指北针针头
- 指北针顶部角度数字
- `heading-up` 自动转图

在一次围绕顶部提示窗、传感器显示链和性能优化的修改后，出现了以下问题：

- iOS 端偶发正常，偶发异常
- Android 端罗盘长期无样本
- 指北针不转
- `heading-up` 自动转图一起失效

## 最终结论

这次问题的主因不是算法本身，而是：

**Android 微信环境下，罗盘监听需要被持续保活；之前将多处看似冗余的 `compassController.start()` 清理掉后，Android 的罗盘样本链被破坏了。**

也就是说：

- iOS 对罗盘监听更宽容
- Android 对罗盘监听更脆弱
- 之前稳定，不是因为链路更“干净”，而是因为老代码里存在一条实际有效的“罗盘保活链”

## 现象总结

### 失效期

- Android 调试面板里 `Compass Source` 为 `无数据`
- iOS 仍可能有 `罗盘` 样本
- 若强行用 `DeviceMotion` 兜底，会出现：
  - 指针会转
  - 但方向不准
  - 自动转图方向错误

### 恢复后

- Android `Compass Source` 恢复为 `罗盘`
- 指北针针头恢复
- 顶部角度数字恢复
- `heading-up` 恢复

## 误判过的方向

以下方向在本次排查中都被考虑过，但最终不是根因或不是主要根因：

### 1. `DeviceMotion` 兜底方案

问题：

- `DeviceMotion` 可以给出设备姿态角
- 但不能稳定代替“指向北”的绝对罗盘
- 用它兜底会导致：
  - 能转
  - 但方向明显不准

结论：

**`DeviceMotion` 不能作为正式指北针来源。**

### 2. 加速度计 / 其他传感器互斥

曾排查：

- `Accelerometer`
- `Gyroscope`
- `DeviceMotion`
- `Compass`

结论：

- 加速度计在当前微信 Android 环境下不稳定，已放弃
- 但这不是这次罗盘彻底失效的主因

### 3. 算法问题

曾尝试调整：

- 角度平滑
- 设备方向单位解释
- motion fallback 算法

结论：

这些会影响“顺不顺”、“准不准”，但**不能解释 Android 完全无罗盘样本**。

## 真正修复的方法

将之前被清理掉的多处 `this.compassController.start()` 恢复回去。

这些调用点主要分布在：

- `commitViewport(...)`
- `handleTouchStart(...)`
- `animatePreviewToRest(...)`
- `normalizeTranslate(...)`
- `zoomAroundPoint(...)`
- `handleRecenter(...)`
- `handleRotateStep(...)`
- `handleRotationReset(...)`

这些调用在代码审美上看起来像“重复启动”，但在 Android 微信环境里，它们实际上承担了：

**重新拉起 / 保活罗盘监听**

的作用。

## 当前工程判断

本项目当前应当采用以下原则：

### 1. 罗盘主来源只使用 `Compass`

不要再让：

- `DeviceMotion`
- 其它姿态角

参与正式指北针和自动转图的主方向链。

### 2. `DeviceMotion` 只保留为辅助或调试输入

可用于：

- 调试面板显示
- 设备姿态观察
- 未来原生端姿态融合参考

但不要直接驱动指北针。

### 3. Android 端罗盘需要保活

后续不要再把这些 `compassController.start()` 当成纯冗余逻辑随意清掉。

如果要优化代码，应该：

- 保留现有行为
- 将其收口为有明确语义的方法

例如：

- `ensureCompassAlive()`
- `refreshCompassBinding()`

而不是直接删掉。

## 与生命周期相关的硬约束

以下约束必须保持：

### 单实例

页面层必须保证任意时刻只有一个 `MapEngine` 活跃实例。

### 完整销毁

`MapEngine.destroy()` 中必须完整执行：

- `compassController.destroy()`
- 其它传感器 `destroy()`

防止旧监听残留。

### 调试状态不应影响罗盘主链

调试面板开关不应再控制：

- 罗盘是否启动
- 罗盘是否停止

否则容易再次引入平台差异问题。

## 推荐保留的调试字段

以下字段建议长期保留，便于后续定位：

- `Compass Source`
- `sensorHeadingText`
- 顶部角度数字
- `heading-up` 开关状态

其中 `Compass Source` 至少应显示：

- `罗盘`
- `无数据`

避免再次将问题误判为算法问题。

## 后续优化建议

如果后面要继续优化这段代码，推荐方向是：

### 可做

- 将分散的 `compassController.start()` 收口成命名明确的方法
- 为 Android 罗盘链补一层更可读的“保活机制”注释
- 保留当前稳定行为前提下做重构

### 不建议

- 再次移除这些重复 `start()` 调用
- 用 `DeviceMotion` 正式兜底指北针
- 让调试开关影响罗盘主链启动

## 一句话经验

**在微信小程序里，Android 罗盘监听的稳定性比 iOS 更脆；某些看似冗余的 `start()` 调用，实际是平台兼容补丁，不应该在没有真机回归的情况下清理。**



