状态四在一个 API 上触发通知,在另一个 API 上却另有所指,而代码看不出任何区别。NetworkManager 里的每个状态值都属于一个有名字的枚举,而不同枚举对同一个数字的含义并不一致。因此,把一个能跑通的片段里的整数复制到另一个回调里,并不是在转移行为,而是在等待一个迟早会破裂的巧合。先找到类型,数字才读得懂。

没有类型的数字只是传闻

这里有两个枚举值得注意。一个描述一般的活动连接:未知、正在激活、已激活、正在停用、已停用。另一个专门描述 VPN 连接,沿途的站点更多,包括准备、连接建立、IP 配置,以及在任何东西被激活之前的身份验证请求。两者的编号都从零开始,然后分道扬镳,所以通用枚举里表示「已停用」的值,会落在 VPN 序列中间的某个位置。两个头文件都没有错,它们回答的是不同的问题。

使用已安装头文件或绑定里的符号常量,而不是字面量。名字能挺过重新编号,整数不能。

盯住一条连接,而不是整台机器

从你在意的配置文件 UUID 和那次激活所创建的活动连接对象开始。设备状态,或者另一条隧道状态,都不能替代它,即使两者在同一刻变化。把对象身份和观测一起保留,这样后续报告才能说出是哪个配置文件产生的。机器有很多状态;通知只讲其中一种。

然后决定这条通知应该表示什么。从未到达激活的连接是建立失败,而曾经活动、随后消失的会话是丢失。这两者值得用不同的词。

说清楚你报告的是哪次转换

保存上一个状态并与新状态比较,而不是孤立地检验新值。进入失败状态和离开已激活状态是不同的事件,即使两者最后都是什么都没连上。把原因字符串一并记下,因为那往往是用户唯一能据此行动的部分。

有意的断开不是故障。如果通知分辨不出来,它只会训练人们忽略它。

让观察者活下去

对没有变化的状态,重复轮询不得再报一次警。这一条规则就去掉了这类通知器产生的大部分噪音。

还要处理管理器重启、被观测对象被替换的情况。绑定在过时身份上的观察者,恰好在有事情发生时安静下来,而安静看起来像健康。管理器存在状态变化后,重新发现当前对象,并记录重新发现发生的时间。否则日志里只剩一条不再上报的隧道。

把通知送到用户所在的地方

有特权的系统回调不会继承桌面会话的通知服务。从 root 级处理器发出的消息可能悄无声息地消失,于是通知器手工测试能用,实际从不用得上。把它送进拥有桌面的会话,或者让桌面自己的客户端去发通知。

如果客户端已有的通知已经覆盖这种情况,就优先用它,因为一个事件配两个通知器是支持问题,不是冗余。把事件时间、配置文件和原因留在支持记录里,密码和完整配置文件不要写进去。记住通知是什么:对已观测状态的报告,而不是强制执行机制;隧道不在时阻断流量,是另一套有自己实现的策略。

相关文章