用途
该技能提供一种固定写法,用于处理“缺陷出在无法打补丁的上游库中”的情况。目标不是把规避方案写得更聪明,而是让它在未来可以被安全删除,避免临时补丁逐渐固化,最终被误认为是有意设计。
主要能力
模板要求把以下五项内容都写在工作代码旁的注释里:
- 机制:追踪到了哪一步,以及在哪里停止;停止的位置本身也是结论的一部分。用错误的“为什么”支撑正确的建议,会让下一位维护者按错误的“为什么”行动。
- 取舍:逐项列出你已知放弃了什么。
- 逃生通道:规避方案自身失效时,行为应降级而不是静默失败。
- 移除条件:写成外部人员也能核查的表述,例如“上游完成 X 后移除”。
- 为什么是本项目的用法暴露了它:这通常也是上游无法复现的原因。
使用方式
示例场景中,应用仅在某操作系统上运行一段时间后崩溃,插入或拔出耳机可稳定触发。追踪库源码后发现:初始化音频输出会注册一个指向该音频对象的进程级设备变更监听器,初始化错误路径返回时没有注销它;调用方随后放弃该对象,而负责注销监听器的清理逻辑只在初始化成功后设置的标志存在时才执行,因此失败的初始化留下指向已释放对象的监听器,并在后续设备变更时被调用,造成原生崩溃。该项目按媒体项创建音频输出且切换时同时运行两个,所以任意一次初始化失败都会使整个会话进入风险状态。
规避方式是将音频后端固定为不注册此类监听器的实现,并保留尾随逗号使自动探测仍可作为后备,从而把失败降级为不同的音频输出,而不是静音。代码示例如下:
// adapted
if (Platform.isMac()) {
// Trailing comma: keeps the library's normal auto-probe as a fallback, so a failure
// here degrades to different audio rather than to silence.
option("ao", "avfoundation,")
}
文中还给出区分“选项不存在”与“设置失败”的写法,避免把两者合并为同一条警告:
// adapted
val rc = lib.set_option(ctx, name, value)
if (rc == ERROR_OPTION_NOT_FOUND) {
Logger.d(TAG, "option '$name' absent in this build — already off, nothing to do")
} else if (rc < 0) {
Logger.w(TAG, "set_option($name=$value) failed: ${lib.error_string(rc)}")
}
适用场景
适用于依赖第三方库且缺陷无法立即在上游修复、需要临时规避并计划后续移除的开发场景,尤其是缺陷只在特定平台、特定调用方式或特定生命周期下出现的情况。
限制与注意事项
没有移除条件的规避会变成永久代码;逃生通道不是重试,而是让功能保持可用替代;不应从症状反推机制,应阅读库源码的错误分支;若库提供日志通道应订阅,否则关键警告可能完全不可见;写注释前应确认上游当前状态,并用可核查的版本表述;规避应保持在能修复问题的最小范围,避免影响从未出现该缺陷的平台。验证时应刻意复现触发条件而非崩溃本身,确认所声明的具体行为确实改变,并故意破坏规避以确认降级路径生效;每次升级依赖时复查移除条件,例如用 grep 搜索相关注释关键字,确保得到简短可读的列表。