HarmonyOS ArkUI 轨交换乘台(07/08):收藏路线失效诊断与版本迁移【共创稿事节】
常用通勤路线被收藏以后,用户希望下次点开就能直接看到行程。麻烦出在站网并不静止:一个换乘通道关闭、一段线路被拆分,或者离线增量合并后站点版本已经改变,旧收藏保存的边序列仍然可以正常读取,却可能指向当前不能通行的路径。这个问题比重新搜索更隐蔽,因为收藏列表往往给人一种“已经确定”的感觉。轨交换乘台前面已经处理过站网版本、封站通告和离线合并,这一篇把这些机制接到收藏入口,重点是怎样诊断旧路线,再让使用者决定是否迁移。

一、收藏的是决策结果,不是永久有效的通行许可
先看一条工作日常用路线:东门经市政广场到湖滨码头,收藏时的站网版本为16,依次使用E2、E8两条通行边。现在站网已经更新到17,E8因为通道调整而关闭。按照原来的列表写法,页面读出站点名称与预计到达时间,再把收藏卡片标成“可直接出发”。这种判断缺少一个关键输入:当前生效的图版本。
我更倾向于让收藏承担“偏好”的职责,而不是把它包装成最新路线。它应保存起点、终点、当时的站点顺序、边顺序、可达性偏好和生成它的图版本。收藏时间用于解释数据来源,不能参与通行可达性判定;展示时长属于历史估计,在新图上需要重新计算。这样站网升级以后,即使路线结构已失效,也能保留用户原来的出行意图。
还要分清两个问题:版本旧了,不一定路线就坏了;版本相同,也不保证客户端内存数据没有被错误覆盖。因此检查必须同时看版本与拓扑。只比较数字会把仍然可走的收藏误判为失败;只看边是否存在,则可能漏掉边已标记关闭或站点衔接顺序改变。
二、给收藏和站网建立明确的数据契约
要解决的问题是:收藏既能被持久化,又能在重新打开时接受新图的校验,而不会将历史估计时间和实时可达性混在一起。这里使用普通ArkTS接口描述业务数据,图版本来自前篇已经维护的站网快照。
export interface SavedRoute {
id: string;
name: string;
originId: string;
destinationId: string;
stationIds: string[];
edgeIds: string[];
graphVersion: number;
accessibility: boolean;
savedAt: number;
}
export interface RailEdge {
id: string;
fromId: string;
toId: string;
open: boolean;
accessible: boolean;
}
export interface RailGraphView {
version: number;
stationIds: string[];
edges: RailEdge[];
}
export type RouteHealth = 'ready' | 'refresh' | 'blocked';
接口字段尽量保持稳定,不在收藏里放UI组件、页面对象或异步回调。stationIds和edgeIds一起保留,是为了追溯每段边的首尾关系;accessibility让收藏时的无障碍偏好能在迁移时继续生效。新版本增加字段时,读取层应提供有意义的缺省值并校验数组内容,不能靠界面随意补上一条线路。对于从旧格式迁来的记录,先转换成完整结构,再交给统一的判定函数。
数据生命周期上,站网快照可以整体替换,旧收藏对象则维持历史内容。不要在收到封站通知时原地修改edgeIds,否则一次错误通知会同时破坏用户的旧方案与新建议。收藏读取失败、记录格式不合格或站网缺失时,列表显示“待核对”并允许查看历史站点,不跳转到出发页。
三、失效诊断要检查每一段边
要解决的问题是:判断收藏能否继续使用,并给出可追溯的原因。一次诊断先建立当前站点和边的索引,再按收藏中的边顺序核对端点、通行状态和无障碍条件。完整性错误优先于版本提示,避免将真正断开的路径标成普通的“可刷新”。
export function inspectSavedRoute(
saved: SavedRoute,
graph: RailGraphView
): RouteHealth {
if (saved.stationIds.length < 2 ||
saved.edgeIds.length !== saved.stationIds.length - 1) {
return 'blocked';
}
const stations = new Set<string>(graph.stationIds);
for (let i = 0; i < saved.stationIds.length; i++) {
if (!stations.has(saved.stationIds[i])) {
return 'blocked';
}
}
for (let i = 0; i < saved.edgeIds.length; i++) {
const edge = graph.edges.find(e => e.id === saved.edgeIds[i]);
if (edge === undefined || !edge.open ||
(saved.accessibility && !edge.accessible)) {
return 'blocked';
}
if (edge.fromId !== saved.stationIds[i] ||
edge.toId !== saved.stationIds[i + 1]) {
return 'blocked';
}
}
return saved.graphVersion === graph.version ? 'ready' : 'refresh';
}
对这种规模的收藏,find逐段扫描通常足够直观;收藏成千上万条时,再在当前快照上构建Map索引,避免每次都线性扫描。索引应当绑定快照版本,快照替换时一并失效,不能沿用旧缓存。E8关闭以后返回blocked,即使保存时的路径只有两段也不例外;如果图升到17而E2、E8依旧可达,则返回refresh,提示重新估算候车和到达时间。
这里的ready指结构在当前快照下符合规则,并不等于未来整个出行时段持续畅通。时刻表的候车计算还需要前篇的班次输入。定位异常时,先打印收藏ID、所用图版本、失败边ID和判定分支;不要把整份站网数组反复输出到HiLog,既影响定位效率,也让敏感位置数据流转范围扩大。
四、迁移必须保留用户确认这一道关
要解决的问题是:重规划完成以后,旧收藏如何迁到新版本,又不会在计算途中被另一份更新覆盖。路线替换至少有两个前提:新路线属于当前最新图版本,并且使用者确实确认了新路径。它不能因为后台发现了一条更快的路,就悄悄改掉一条用户长期使用的无障碍路线。
export interface RouteSuggestion {
graphVersion: number;
stationIds: string[];
edgeIds: string[];
}
export function acceptRouteMigration(
saved: SavedRoute,
suggestion: RouteSuggestion,
currentVersion: number,
confirmed: boolean
): SavedRoute | undefined {
if (!confirmed || suggestion.graphVersion !== currentVersion ||
suggestion.stationIds[0] !== saved.originId ||
suggestion.stationIds[suggestion.stationIds.length - 1] !== saved.destinationId) {
return undefined;
}
return {
...saved,
stationIds: [...suggestion.stationIds],
edgeIds: [...suggestion.edgeIds],
graphVersion: currentVersion
};
}
这段函数负责形成迁移后的数据副本,建议路线本身还必须先经过前述图校验与路径规划约束。真正保存时,以id和收藏修订值做条件更新;如果另一页面已经替换了收藏,当前页面要重新加载并提示差异。迁移失败保留原记录,同时保持推荐路线为临时数据;成功后再更新可见列表和本地存储。这样网络返回慢、页面离开或站网继续升级,都不会自动改写历史。

五、列表状态、异步结果与页面销毁的衔接
收藏页可将展示状态拆成“读取中、待刷新、不可用、可用”。读取时先根据本地记录渲染名称,再用当前站网快照完成核对。如果路由计算在后续返回,应携带发起时的图版本和查询序号:图已经升级,旧查询就只保留诊断信息,不参与当前推荐卡片刷新。这个设计与第四篇的请求代际隔离是一致的,但收藏还多了一道用户确认动作。
ArkUI层要明确状态通知方式。采用状态管理V2时,可以让展示模型使用@ObservedV2和@Trace关联字段;如果列表只是纯值结构,也可以每次将新的卡片数组整体替换。不要把未受观测的普通对象深层字段改完,就期待列表自动更新。还需给列表条目稳定的收藏ID,避免排序变化后复用错误的卡片状态。
页面离开时,清理页面级回调和不再需要的订阅;正在计算的业务结果即使之后返回,也通过查询序号与页面有效标记拒绝提交。切到后台再回来,应优先检查站网版本,而不是重复触发所有收藏的完整路径搜索。缓存判断与页面生命周期处理分开,未来扩展到平板双栏界面时才不会出现左栏已更新、右栏仍显示旧建议的问题。
六、权限、兼容和数据保存的取舍
收藏校验本身只涉及应用已经拥有的路线数据与业务算法,不需要为了这一步新增定位、相机或联系人权限。若未来加入“当前位置作为起点”,应在真正进入相关功能时再按定位能力的使用场景处理授权;拒绝授权时依然保留手动选择站点入口。保存的路线名称、站点编号和无障碍偏好也应避免成为与出行身份绑定的长时间追踪记录。
本地存储需要控制数据格式版本、写入失败处理和重复点击。收藏可以采用结构化记录保存,但不要直接序列化带有状态装饰器的对象实例;先转为纯数据结构,再按照当前应用选定的ArkData持久化方案写入。容量小的时候,简单存储更容易维护;需要按收藏ID检索、修订冲突控制和大规模历史迁移时,关系型存储更适合。无论采用哪条路径,都要把“写入成功”与“页面已更新”视作两件事。
七、用对照矩阵定位问题,而不是看一眼界面就结束
建议把核对条件整理为独立输入:图16到图17、E8关闭、仍然开放但版本更新、缺失站点、边方向被修改、无障碍边取消、迁移确认时图再升到18,以及两次快速点击“替换路线”。每个输入都明确期望状态、失败边与保存动作。这样发现错误时,先辨别数据格式、图快照、算法还是列表提交链路,而不是在页面中临时加判断掩盖问题。
这里尤其要检查“新建议通过计算,保存前版本再变化”的竞态。正确做法是拒绝旧建议并重新规划,不应只把返回对象的graphVersion改成最新数字。也应覆盖离线状态:只存在图16时,卡片可以显示最近可读的历史路线,但“当前可通行”应依赖可确认的新图;恢复连接后先完成快照合并,再开放迁移入口。

八、把收藏页设计成可信的路线入口
收藏功能真正需要解决的不是减少一次点击,而是减少错误信任。旧路线可追溯,新图有版本,失效原因能落到具体边,迁移有明确确认;这四个条件连起来,才允许将收藏从静态文本变成出行入口。后续若加入跨设备收藏同步,还要为收藏本身增加修订序号,并把“收藏内容冲突”和“线路版本更新”分别处理,不能拿一个时间戳覆盖两种变化。
从工程成本看,这一篇没有增加复杂的系统能力,核心是把已经存在的图快照、路径校验、异步隔离和状态刷新连成闭环。对于换乘图持续变化、用户依赖常用路线的应用,这种可诊断、可迁移且保留旧记录的设计,比自动把所有收藏改成当前最快路线更稳妥。
相关接口资料可参考华为开发者文档:ArkTS简介、https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-v5/arkts-new-observedv2-and-trace-V5@ObservedV2与@Trace、https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/arkts-watch@Watch状态变更通知。
更多推荐


所有评论(0)