这里展出的不只是做完的东西。
正在推进的想法、卡住的难题、犯过的错误——它们同样是成果,甚至更接近工作的真相。
「成果展」这三个字,容易让人误会。好像非得等东西彻底做完、打包发版、盖上章,才配拿出来给人看。
但真实的工作不长那样。它更多时候是:一个想法在脑子里转了三天,一个 bug 反复改了三轮才找到真正的病根,一个方案调研完了却决定「先存着,有兴趣再做」。这些东西没有终点,却占据了绝大部分的时间和心力。
所以这场展览换了个策展逻辑——把「进行中」放在第一展区,把「已落地」放在第二。因为正在推进的事,才是此刻真实的工作状态;而那些做完的,不过是它们的昨天。
第五展区是「错题墙」,展出 30 条犯过的错。这是整场展览我最想请你走进去看的地方——一个人(和一个 AI)能把自己的错误明明白白挂出来,说明这些错真的被消化过了。
四条同时推进的线。每一件展品都如实标注了现在到哪一步、下一步做什么、以及卡在哪儿。卡点不回避、不美化,因为那正是工作此刻的形状。
四份已交付的作业与一个已发版的 App。放在第二展区不是因为它们不重要,而是因为它们的价值已经兑现,不再需要被讨论。
dev=1 与百度 coord_type=wgs84 都能直接吃 WGS-84 自动转;腾讯不支持,必须预先转 GCJ-02;Google Maps / 奥维走 geo: 原生 WGS-84。这个展区可能最反直觉:调研做完、方案成型,然后主动决定「先存着」。这不是烂尾,是判断——知道什么时候该停手,和知道什么时候该动手同样重要。
这是本次展览的核心展品。十一个版本里,有三次是纯粹的翻车与回滚。展开任意一行,能看到那次到底发生了什么、根因在哪、最后怎么修的。
基于 hurui200320 / NSG(AGPL-3.0)二次开发,目标是给尼康相机通过蓝牙注入 GPS 坐标。零第三方 SDK,8 个核心依赖全是 AndroidX——这是刻意的架构选择,不是偷懒。
BLE 连接与 GATT 写入跑通。定位只用 Android 原生 LocationManager 的 GPS_PROVIDER,不引融合定位、不依赖 GMS,回退链 GPS → NETWORK → PASSIVE 含 30s 缓存。
这一版能用,但连接体验有硬伤。正是为了还这笔债,才有了下一版的激进改动——以及随之而来的翻车。
为解决旧债,在 connectCurrentDevice() 里新增重试 / 超时 / ACL 接收器。但这个方法被守望门、绑定回连等多条路径共用——异步 retryJob 和 isRetrying 标志把其他路径全堵死了。
v0.5.2 果断回滚到 v0.4.x 的同步直连。领悟到:守望门本身(5 秒间隔扫描 + 同步直连)就已经是重试机制,在直连内部再叠一层 15s 超时重试,是重复叠加,只会放大延迟。
期望蓝牙适配器就绪后能自动后台连接,于是把 autoConnect 从 false 改成 true。结果 OriginOS / Android 16 实机直接 GATT_ERROR。
改回 false。教训:autoConnect=true 不是所有 Android BLE 栈都支持,status=135 是厂商固件的已知不兼容信号,不能假定 AOSP 行为。而且守望门直连本来就不依赖它——获取已配对设备句柄再同步直连,已经跳过了扫描。
守望门从「BLE 扫描」改成「直连」时,break 条件忘了同步改,仍含 Connecting。第一轮调完直连状态变 Connecting,第二轮循环直接命中 break 退出——守望门跑一次就停,相机休眠后闪退。
改完 break 条件闪退依旧。深挖发现 onConnectionStateChange() 在 status 异常时先 emitError(),但没有 return,继续执行 when(newState) 又发了 Disconnected——同一个回调发射两个事件,每个都启动守望门,最终在 BLE 原生栈上形成快速 connect/close 循环,native crash。
错误分支加显式 return。沉淀出两条通则:① 循环里调用了会修改判断条件的操作 = 潜在 bug 模式;② if (error) { handle() } 后面没有 else / return = 潜在的重复处理。
日志偶发顺序混乱:后台返回后倒序变顺序,或者「最新一条在顶,但下面是最初那条」。改 reverseLayout、改 stable key、改 LaunchedEffect,都没根治。
Application 单例用 .add() 追加(最早→最新),ViewModel 用 prepend(最新→最早)——两份缓存顺序相反。重新绑定 Service 时加载历史,顺序就错乱了。
改为单一数据源。最大的教训是:之前一直在 UI 层修数据层的病,治标不治本。另外顺手修了 sendingGeo 异步守卫——writeGeo() 返回 ≠ GATT 写完,标志位必须在回调里释放,还得配超时兜底。
基于 v0.7.0 这个真机验证可用的版本作为发布基底。发布前重新克隆上游仓库,逐项核对 AGPL-3.0 版权与作者归属没有被覆盖——开源合规不是走过场。
三条线并行:nsg(开发沙盒)/ zpin(对外分发)/ zpin-person(个人内部版)。三个包名不同,可同机共存,互不干扰。
接入高德地图 SDK,右滑进地图、左滑回日志。踩到三个坑并沉淀成规范:隐私合规接口 updatePrivacyShow/Agree 必须先调(否则 errorCode 555570)、MapView 生命周期三件套要处理、R8 得加 -dontwarn net.jafama.FastMath。
体积膨胀到 74.5MB,且不敢开 R8。这正是「展区三」里那两个待启动方案要解决的问题——问题被清楚地记下来,交给了未来。
公开版新增「在地图中查看 GPS 点位」:高德 / 百度 / 腾讯 / Google Maps 自动坐标转换;体积反而压到 9.0MB——因为走的是 URI 跳转本机地图,不再内嵌 SDK(正是「展区二」那张已落地的方案)。
修首次启动权限崩溃、尼康 ZF 序列号识别异常;优化权限申请流程、主页按钮布局、新启动图标。
这些错误全部来自真实的开发日志与两份 DEVLOG。挂出来不是为了自嘲,而是因为被写下来的错,才算真正被消化过。以下是其中最值得看的九条。
Connecting。而直连模式下「守望门自己连」和「别人在连」是同一个操作,条件变成了自我否定——跑一轮就退出。这些规矩没有一条是一开始就定好的,全是踩坑之后反推出来的。它们现在被固化在项目记忆里,每次开工前先过一遍。
任何对代码、文档、配置的修改,先用文字列清楚:改哪些文件、改什么、预期效果、风险点。确认之后才执行。严禁「先改完编译交付再告知」。
拿到新图先做像素 / 结构分析(背景、主体、透明区),再定融合方案。Logo 白边来回折腾三轮,就是因为一开始靠猜。
HTML 标签闭合校验、node --check 语法检查、场景化逻辑自检(有效 / 错密码 / 篡改 / 过期 / 无 cookie)。Worker 的两个隐蔽 bug 就是靠六场景自检才逼出来的。
版本线由人定义,AI 不擅自升号、不擅自建新版本目录。曾经擅自把 0.1.2 升到 0.1.3 还建了归档目录,被要求全部合并回滚。
后台跑构建,按预估耗时等够了再查一次,不连续轮询、不干等通知。预估 1.5–2 分钟,那就 3 分钟后查。
任何改动落地后,在回复之前必须同步更新三份文件:版本 README、DEVLOG、工作日志。漏一份即事故。
这是多轮迭代之后写在 DEVLOG 里的一句话,也是这场展览的结语。
三个月,39 个工作日,11 个版本,30 条错误。最有价值的产出不是那个 9.0MB 的安装包,而是那些被写下来、下次不会再犯的东西。