尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MiroFish:开源离线钓鱼记录与钓点规律分析工具

MiroFish:开源离线钓鱼记录与钓点规律分析工具 MiroFish 这个项目最初的起因特别朴素我钓鱼有七八年手机相册里躺着上千张鱼获照片备忘录里零零散散记着某某水库东岸下午三点鲫鱼几条可真到了要复盘的时候翻相册翻到手指发酸也理不出个头绪。MiroFish 就是我想解决这个问题做出来的东西——它把钓况记录、钓点沉淀、规律分析这三件事串成一条链路主打开源、离线可用、数据存本地。说白了它不教你怎么钓鱼它帮你把已经钓过的每一次都变成可检索、可统计、可复用的资料。这篇文章写给两类人一类是钓龄不短、手里攒了一堆零散记录但从来没系统整理过的钓友另一类是想做一个离线优先 地图可视化小应用的开发者MiroFish 的技术取舍为什么选 Flutter、为什么地图不用现成大厂 SDK、为什么坚持 SQLite踩过的坑和最后的结论你可以直接抄。后面我会把数据表设计、指标公式、关键交互、离线同步、定位漂移排查这些东西一次性讲透能上手的部分我都会给可跑的代码和参数。1. MiroFish 到底解决什么问题从钓完就忘到数据说话很多人听到钓鱼记录 App第一反应是这玩意儿有必要吗。我一开始也这么想直到我把自己的记录方式挨个试了一遍才发现问题不在记不记而在记了之后能不能用。1.1 纸质记录、备忘录、相册为什么都撑不住先说我踩过的三种记录方式。第一种是本子。手写确实有仪式感但它的致命伤是不可检索。你想查去年十月在哪个钓点用红虫钓到过五斤以上的鲤鱼只能一页页翻。一个季度之后本子就开始积灰因为查询成本高到超过收益。第二种是手机备忘录。好处是随身、可搜索坏处是结构完全自由。我自己的备忘录里出现过今天口不错、东边水深、老张说这里出过草鱼这种句子三个月后我根本不知道口不错到底是几尾东边到底是哪个坐标能不能复现。自由文本在记录时很爽在分析时是灾难。第三种是相册。照片记录了鱼获但没有任何环境变量。同样是三斤的鲫鱼水温 8 度和水温 22 度、气压 1018 和气压 1002背后的规律完全不同。照片不会告诉你这些而恰恰是这些才是能复用的信息。注意记录工具最大的敌人不是记不全而是记起来太麻烦。任何需要你在水边掏手机、解锁、进三级菜单、填七个字段的方案最后都会死掉。这是 MiroFish 所有交互设计的出发点。1.2 三类用户三种真正想要的东西我把目标用户拆成三类因为他们的需求会直接决定功能优先级。第一类是入门一两年、还没摸到规律的人。他们要的不是复杂统计而是我上次在这个位置钓得怎么样。所以他们最需要的是钓点维度的聚合视图这个点我去过几次、总共多少尾、平均体型多大、什么时段出鱼。这类用户占比最大也最容易流失所以首屏必须一眼看懂。第二类是有五年以上经验的老钓手。他们心里其实已经有一套经验模型缺的是验证工具。他们会关心某个月份、某个水位区间、某种饵料下的效果差异也就是多维度交叉筛选。这部分用户是留存核心。第三类是数据控愿意为一条精准记录多花十秒。他们要的是原始数据可导出、指标定义透明、能自己拿去跑分析。这也是我坚持开源和 CSV 导出的原因。1.3 我主动砍掉的功能以及为什么做一个项目决定不做什么比决定做什么更重要。MiroFish 从第一版到现在我明确砍掉了四类东西。不做社交。一旦有钓友动态、评论、点赞就要有服务端、内容审核、用户体系整个项目的复杂度会从个人工具直接跳到小型平台维护成本翻十倍。而社交价值对记录本身几乎没有加成。不做实时水情付费接口。很多水质、水位、溶氧数据来自商业气象服务接入要为每条请求买单还要处理各家字段不统一。我的做法是允许用户手动填写、或者从公开的免费气象接口拿基础数据拿不到就留空绝不因为缺字段阻塞记录流程。不做饵料电商导购。这是最容易变现但也最容易让工具变味的路径。一旦你推荐饵料记录的客观性就会被质疑。不做云同步强绑定。我提供同步能力但绝不要求注册才能用。本地是唯一可信数据源云端只是备份和跨设备通道。这条原则后面在冲突处理那一节还会展开。边界划清之后你会发现代码量少了、决策也快了。这一点在个人项目里尤其重要——你唯一稀缺的资源是持续的注意力。2. 技术选型为什么最后落在 Flutter SQLite MapLibre 这套组合技术选型这件事我在 MiroFish 上前后推翻过一次。第一版是纯原生 Android写到一半发现 iOS 用户包括我自己另一台手机完全用不了于是重做。这套组合是重做之后稳定跑了两年多的方案下面把每个选择的对比过程摊开讲。2.1 跨端方案Flutter、原生、小程序的真实差异维度Flutter双端原生小程序一次开发覆盖端Android / iOS / 桌面各自实现工作量翻倍仅限平台生态内离线数据库支持优秀sqflite/drift 直连 SQLite优秀受限本地存储容量与类型都不够地图自定义能力中等偏上可嵌原生视图最强最弱相机 / 定位 / 文件插件成熟最成熟受平台限制适合个人维护是否是但受平台约束我最终选 Flutter核心理由只有一条离线优先的本地数据库必须是一等公民。小程序虽然开发快但本地存储是 KV 结构、容量有限做不了复杂 SQL 聚合而 MiroFish 的价值恰恰在聚合查询上。双端原生当然最自由但一个人维护两份代码两个月后必然只更新一端。Flutter 的坑也要说清楚。相机、定位这类原生能力依赖插件插件质量参差不齐遇到问题你得能读一点原生代码。另外 Flutter 的包体积比原生略大第一版打出来安装包 30 多 MB对一个记录工具来说不算小。我的处理是开启拆分 ABI、剥离调试符号压到 20 MB 以内能用就行。2.2 地图选型为什么不直接上大厂 SDK钓点可视化是 MiroFish 的门面地图选型我纠结最久。方案优点缺点我的结论大厂地图 SDK底图质量高、POI 全依赖密钥、有配额、离线能力弱、包体大用在导航到钓点这一条支线MapLibre 自备瓦片完全可控、可离线、样式随便改需要自己找瓦片源主视图用它静态图片拼接实现最简单不能缩放定位、无法交互只适合做分享图我最终采用双轨主界面用 MapLibre底图用离线 MBTiles 包用户可以在设置里下载自己常去区域的瓦片当用户点击去这里需要路径规划时再拉起系统地图或大厂 SDK 完成导航。这样既保住了可控性和离线能力又不至于在导航体验上跟大厂硬碰。瓦片这块有个细节值得说。一张 512×512 的栅格瓦片覆盖一个中型水库周边约 40 平方公里大概需要 200 到 400 张瓦片压缩后 15 MB 上下。这个体积对手机来说可以接受我把它做成用户主动下载某个区域而不是全量预置。2.3 数据存储为什么不用 JSON 文件第一版我用的是 JSON 文件存记录写到 300 多条的时候就开始卡。原因很简单每次写入都要全量序列化和反序列化而且任何统计都得把整份数据读进内存再算。换成 SQLite 之后情况完全不同。3000 条记录下一条带条件的分组统计查询在手机上基本是 10 毫秒级别。SQLite 的优势在于它能做索引、聚合、事务这三件事恰好对应我要的快速筛选、统计指标、批量写入不脏数据。几个具体的工程决定驱动选 drift 而不是裸 sqflite。drift 提供类型安全的查询构建和编译期检查字段改名时编译器会直接报错这对长期维护太重要了。开启 WAL 模式。PRAGMA journal_modeWAL;让读写在多数场景下不互相阻塞记录页一边写、首页一边刷新统计不会打架。外键约束手动开启。SQLite 默认不启用外键每次连接后都要执行PRAGMA foreign_keysON;这个坑我在测试阶段踩过删了钓点之后孤儿记录一直留着。PRAGMA journal_mode WAL; PRAGMA foreign_keys ON; PRAGMA synchronous NORMAL;实操心得synchronous我设成 NORMAL 而不是 FULL。FULL 每次事务都强制刷盘写一条鱼获要多等几十毫秒在水边单手操作时体感很差。NORMAL 在 WAL 模式下已经能保证崩溃不丢已提交事务对个人记录场景足够。2.4 分层架构把能不能复用想清楚再动手MiroFish 的代码分三层UI 层页面与组件、领域层指标计算、业务规则、数据层DAO 与本地库、同步。分层的判断标准很简单如果我把界面从手机换成平板改动只应该发生在 UI 层。实践下来收益最大的其实是领域层。像有效作钓时长这种概念一开始我在页面里随手算后来发现统计页、详情页、导出模块各算一遍三处口径不一致同一次出钓在不同页面显示的小时数差 0.5。把它收敛到领域层一个函数之后这类问题一次性消失。3. 核心数据结构与关键指标计算这一节是 MiroFish 真正的内核。表结构设计得好不好直接决定后面能不能问出有价值的规律指标定义清不清楚直接决定你看到的数据是不是自欺欺人。3.1 四张主表出钓、鱼获、钓点、天气快照我的设计原则是把一次出钓作为主干其他都挂在它下面。因为复盘时你的提问几乎总是那次去某某地方……而不是所有鲫鱼……。CREATE TABLE spot ( id TEXT PRIMARY KEY, name TEXT NOT NULL, lat REAL NOT NULL, lon REAL NOT NULL, water_type INTEGER NOT NULL, -- 1 水库 2 河流 3 池塘 4 湖泊 note TEXT, created_at INTEGER NOT NULL ); CREATE TABLE trip ( id TEXT PRIMARY KEY, spot_id TEXT REFERENCES spot(id) ON DELETE SET NULL, start_at INTEGER NOT NULL, -- UTC 毫秒 end_at INTEGER, break_min INTEGER DEFAULT 0, -- 中途休息分钟数 method INTEGER, -- 1 台钓 2 路亚 3 海钓 4 传统钓 target TEXT, updated_at INTEGER NOT NULL, deleted INTEGER DEFAULT 0 ); CREATE TABLE catch_item ( id TEXT PRIMARY KEY, trip_id TEXT NOT NULL REFERENCES trip(id) ON DELETE CASCADE, caught_at INTEGER NOT NULL, species TEXT, weight_g INTEGER, length_mm INTEGER, bait TEXT, released INTEGER DEFAULT 0, photo_path TEXT ); CREATE TABLE weather_snap ( trip_id TEXT PRIMARY KEY REFERENCES trip(id) ON DELETE CASCADE, temp_c REAL, pressure_hpa REAL, wind_level INTEGER, wind_dir INTEGER, -- 0-7 方位 precip_mm REAL, source INTEGER -- 1 手动 2 接口 );有三个地方是我改了又改才定下来的。第一时间统一存 UTC 毫秒整数。用文本存日期看着可读但排序和范围查询都会退化成字符串比较跨时区还会出乱子。整数时间戳是唯一省心的选择。第二catch_item.caught_at独立于trip.start_at。我一开始只记出钓时间结果发现有价值的信息被丢掉了同一次出钓早上七点和中午十二点出的鱼规律完全不同。加上单尾时间戳之后时段分析这个功能才成立。第三deleted逻辑删除。物理删除在同步场景下是灾难A 设备删了记录B 设备不知道下次同步又推回来。逻辑删除 updated_at是后面同步方案的基础。索引我只加了必要的几条加多了写变慢得不偿失CREATE INDEX idx_catch_trip ON catch_item(trip_id); CREATE INDEX idx_trip_spot ON trip(spot_id); CREATE INDEX idx_trip_start ON trip(start_at);3.2 咬口率、CPUE、钓点复现率公式和手算过程指标不是随便定的我选的标准是能横向对比——换了钓点、换了季节数字仍然可比。CPUE单位时间鱼获单位 尾/小时。公式CPUE 有效鱼获尾数 / 有效作钓时长有效作钓时长需要扣除休息(end_at - start_at) / 3600000 - break_min / 60。举个例子。3 月 12 日东岸钓点8:40 开竿15:10 收竿中间吃午饭休息 50 分钟。时长 6.5 小时 − 0.833 小时 5.67 小时。当天入护 22 尾。CPUE 22 ÷ 5.67 ≈ 3.88 尾/小时。这个数字比今天钓了 22 条有用得多因为它把钓了多久这个变量归一化掉了。上午钓三小时拿 15 条CPUE 5.0和全天钓六小时拿 22 条CPUE 3.88显然是前者效率更高。平均单尾重量单位 克/尾。公式总重量 / 尾数。22 尾总重 6.8 公斤即 6800 ÷ 22 ≈ 309 克。这个指标配合 CPUE 一起看能区分小鱼爆护和大鱼慢口两种完全不同的钓况。我自己就吃过亏——某次 CPUE 只有 0.8看着很差但平均单尾 1.4 公斤实际是很不错的一天。钓点复现率单位 %。公式该钓点有鱼获的出钓次数 / 该钓点总出钓次数。某钓点去过 9 次其中 7 次有鱼获复现率 77.8%。这个数字是判断一个钓点值不值得再去的核心依据比单次爆护更有参考价值。时段出鱼占比。把一天切成若干时段比如每两小时一段统计各时段鱼获占总数的比例。SQL 里用strftime配合本地时区偏移处理SELECT CAST(strftime(%H, datetime(caught_at/1000, unixepoch, localtime)) AS INTEGER) / 2 AS slot, COUNT(*) AS cnt FROM catch_item WHERE trip_id ? GROUP BY slot ORDER BY slot;注意这里必须用localtime。如果直接用 UTC 小时你会在凌晨五点看到出鱼高峰——那是时区错位不是鱼在熬夜。3.3 为什么气象字段要冗余存一份快照一个常见的做法是出钓时只存一个气象接口的查询 ID需要展示时再拉一次。我不这么做原因是历史数据必须冻结。气象数据源会修正历史值接口会变更免费服务会下线。如果你的记录依赖外部查询三年后再打开那次出钓的气压可能是空的或者变成了修正后的数字。而当时的真实气压才是你复盘时想看的那个。所以我的做法是出钓开始时拉一次基础气象数据写入weather_snap表之后永不更新除非用户手动改。字段只留最有用的五个气温、气压、风力、风向、降水。气压我特别看重因为气压变化对鱼类活性影响明显这个字段缺失的话很多分析做不了。如果接口不可用字段留空绝不阻塞记录。这一点看起来是妥协其实是对的宁可有一条气象为空的真实记录也不要因为强依赖接口而丢掉整条数据。4. 关键功能落地从记录到分析的完整链路前面是地基这一节讲盖房子。我会按用户真实使用顺序——记录、同步、分析、导出——把每个环节的实现要点说清楚。4.1 记录页设计3 秒完成一条鱼获记录页是生死线我改过五版。最终版本的交互是这样的打开应用直接落在记录页不是首页。页面顶部只有三个大按钮上鱼、换饵、收竿。点上鱼弹出一个半屏面板里面所有字段都有默认值鱼种默认上次使用的鱼种饵料默认当前竿的饵料重量默认空可跳过时间自动取当前时间可微调也就是说记录一条鱼获最少只需要点两下。照片是可选的后置操作在鱼获列表里长按补传。这个设计背后是我在水边的真实观察出鱼的时候手是湿的、有味道的、还要盯着浮漂能给你的注意力窗口不超过十秒。任何强制字段都会导致记录被推迟推迟就会遗忘。实操心得我把重量设成选填而不是必填一开始担心数据质量下降。实测三个月后填写率是 62%。如果设成必填整体记录量大概会掉一半以上——用一半的记录量换 100% 的重量完整度这笔账不划算。缺失值在统计时用重量样本数单独标注就没有误导。4.2 离线优先与跨设备同步冲突规则必须先定MiroFish 的同步逻辑很简单但规则必须先写下来否则每次同步都是一次数据事故。每条记录带两个元字段updated_at最后修改时间戳和deleted逻辑删除标记。同步流程拉取云端updated_at大于本地last_sync_at的记录逐条比较云端updated_at更新则覆盖本地推本地updated_at大于last_sync_at的记录到云端更新last_sync_at。冲突规则是后写胜last-write-wins按updated_at比较。我知道这个规则粗糙会丢并发修改但对单用户多设备场景足够了——同一秒在两个设备改同一条记录的情况在钓鱼这件事上基本不会发生。删除的处理要特别说一下。删除时不能物理删除而是把deleted置 1 并更新updated_at。同步完成后本地和云端都保留这条墓碑记录只是查询时过滤掉。墓碑可以定期清理比如保留 180 天。4.3 钓点热力图与时段分析图表的实现热力图是 MiroFish 最容易被截图分享的功能实现上其实不难。思路是经纬度网格化聚合。把经纬度按 0.001 度取整作为格子键SELECT ROUND(lat, 3) AS glat, ROUND(lon, 3) AS glon, COUNT(*) AS cnt FROM catch_item c JOIN trip t ON t.id c.trip_id JOIN spot s ON s.id t.spot_id WHERE c.caught_at BETWEEN ? AND ? GROUP BY glat, glon HAVING cnt 2;0.001 度在纬度上大约是 111 米在经度上要乘以 cos(纬度)。以常见的北纬 30 度为例cos30° ≈ 0.866所以经度方向一格约 96 米。这个尺度对钓点来说刚好——比它细就会出现大量单点噪声比它粗就把不同的钓位混成一团。HAVING cnt 2这一句是我的个人偏好只显示至少出过两尾的格子能把偶然路过的噪声过滤掉。时段分析用前面 3.2 的 SQL渲染成柱状图。这里有个展示技巧柱状图的横轴不要从 0 点开始画到 24 点而是只画有数据的时段区间比如 5 点到 20 点。否则大半夜的空柱子会占掉一半屏幕宽度视觉上非常浪费。4.4 数据导出为什么我坚持做 CSV 和 GPX导出功能看着不起眼但它是数据主权的体现。我提供两种格式CSV用于表格分析。导出时把出钓信息和鱼获拍平成宽表一行一尾鱼重复出钓信息。这样丢进任意表格工具里都能直接透视。GPX用于地图工具。把所有钓点按轨迹点格式导出可以在别的支持 GPX 的地图软件里打开方便规划路线。# CSV 导出核心逻辑伪代码领域层 rows [] for trip in trips: for c in trip.catches: rows.append({ 出钓ID: trip.id, 开始时间: fmt_local(trip.start_at), 钓点名称: trip.spot_name, 纬度: trip.lat, 经度: trip.lon, 作钓时长(小时): round(trip.effective_hours, 2), 鱼种: c.species, 重量(克): c.weight_g, 饵料: c.bait, }) write_csv(rows, encodingutf-8-sig)注意CSV 一定要带 BOM也就是utf-8-sig。我第一版用纯 UTF-8 导出Windows 用户打开 Excel 全是乱码反馈里十条有六条在问这个。加上 BOM 之后立刻消停。5. 踩过的坑与排查手册这一节可能是全文最值钱的部分。下面这些问题都是我实际遇到过、并且花了时间才定位的按现象—原因—处理的结构写。5.1 定位漂移钓点跑到河中央现象用户在水库岸边记录钓点地图上显示的位置在水面中间偏移几十米。排查过程先怀疑是坐标系问题。国内地图存在多种坐标系直接混用会有系统性偏移。但我用的是 MapLibre 配自备瓦片两边都是同一套坐标理论上不该偏。接着看定位数据本身。Android 的定位结果里带一个accuracy字段我打印出来发现在开阔水面附近网络定位的精度可以差到 50 到 200 米因为基站和 WiFi 定位在水域附近参考点稀少。而 GPS 冷启动第一帧往往是粗定位几秒后才收敛。处理方案只接受accuracy 30米的定位结果超过就继续等设定最长等待 15 秒超时后提示用户继续等待精确位置 / 手动放大地图选点默认不自动写入弹出确认面板让用户在放大的地图上微调一个十字准星。第三条是关键。任何自动写入的坐标都要经过一次人工确认这一步把偏差投诉降到了零。后来我把这个交互固化成落点确认页反而成了用户喜欢的功能因为顺手可以给钓点起名字。5.2 跨天出钓凌晨那一竿统计全错现象夜钓到第二天凌晨这次出钓的时段分布图表全部挤在最后一段前半夜的数据看不见。原因我最初按trip.start_at的日期来切分时段。一次从晚上 8 点到次日凌晨 3 点的出钓所有鱼获的时间戳虽然不同但都被归到了同一天的槽位里而凌晨 2 点被算成了第二天 2 点跨天的边界处理没有做。处理方案时段统计不再按自然日切分而是**按距开竿的偏移小时数**来分桶。SELECT CAST((caught_at - t.start_at) / 3600000 AS INTEGER) AS hour_offset, COUNT(*) AS cnt FROM catch_item c JOIN trip t ON t.id c.trip_id WHERE t.id ? GROUP BY hour_offset ORDER BY hour_offset;hour_offset 0表示开竿后第一小时内 5表示第五个小时。这样无论是否跨天规律都能正确呈现。而且这个视角其实更符合钓况分析的逻辑——鱼口好不好往往跟开竿后第几小时比跟几点钟关系更大。顺带一提跨天还影响出钓日期的显示。我的处理是如果end_at的日期不同于start_at在列表里显示成3月12日夜 — 3月13日凌晨这种形式而不是简单取开始日期。这个细节虽小但用户能立刻感知到。5.3 数据库膨胀照片把库撑到几百兆现象用了半年应用占用空间从 40 MB 涨到 500 MB 以上备份导出越来越慢。原因我最初把照片以 BLOB 形式直接存进 SQLite。这个做法当时觉得省事一个文件搞定但 SQLite 的 BLOB 会让单页数据变大查询和 VACUUM 都变慢而且整库备份时照片全部被复制一遍。处理方案照片只存文件路径文件放在应用私有目录按trip_id分子目录。数据库里只留photo_path字符串。备份时只导出数据库和 CSV照片单独打包用户可以选择性包含。同时加了两个约束单张照片压缩到长边不超过 1600 像素、质量 85%每次出钓最多保留 20 张照片。压完之后一张照片 300 KB 左右半年数据也就一百多兆可以接受。另外一定要定期执行VACUUM回收空间逻辑删除的墓碑记录清理完之后不 VACUUM文件体积不会变小DELETE FROM trip WHERE deleted 1 AND updated_at ?; VACUUM;注意VACUUM会短暂锁库我把它放在应用启动时的后台任务里不在用户操作路径上执行。5.4 常见问题速查表现象可能原因处理方式钓点坐标偏移几十米定位精度不足、粗定位被采纳只接受 accuracy ≤ 30m加人工确认步骤时段图表数据挤在一起按自然日切分跨天未处理改用距开竿偏移小时分桶CSV 打开是乱码未加 BOM导出用 utf-8-sig 编码应用占用空间暴涨照片以 BLOB 入库改存路径压缩到 1600px统计页和详情页时长不一致指标在 UI 层重复实现收敛到领域层统一函数删除的记录又回来了物理删除 同步冲突改逻辑删除 墓碑机制首页统计刷新时卡顿读写互相阻塞开启 WAL索引只留必要几条地图一片空白离线瓦片未下载或区域不匹配检查瓦片缩放级别与覆盖范围实操心得这张表我建议你在动手写之前先看一遍尤其是指标口径不一致和删除记录回来这两条几乎每个做本地优先应用的人都会踩。它们的共同特征是不会在开发阶段暴露只在长期使用后才显形所以提前规避的收益特别高。6. 这个项目后面还能怎么长MiroFish 现在稳定在记录 分析这个核心闭环上我不打算把它做得更重。但如果有人想接着往下做我看到几条比较自然的方向。一条是离线规律推荐。现有的数据已经足够支撑一个轻量模型给定钓点、月份、当前气压区间从历史记录里找出最接近的若干次出钓把当时的饵料、水层、时段作为参考给出。这不需要联网也不需要大模型本质上是相似度检索加排序用本地 SQL 加一个简单的加权打分就能跑起来。做好之后它的价值不是告诉你该怎么钓而是提醒你上次类似条件下你用的是什么。另一条是多设备协同的改进。目前的后写胜规则对单用户够用但如果要支持两人共享同一个钓点库就需要更细的字段级合并策略。要做的话我倾向于把每次修改拆成字段级事件而不是整条记录覆盖代价是数据量增加、查询复杂度上升得权衡。还有一条是硬件接入。水温、溶氧这类数据靠人工填其实很不可靠如果能接一个便宜的蓝牙水温探头每次开竿自动记录一次数据的可信度会明显提升。这个方向的难点不在软件在于找到一个功耗和稳定性都过关的方案我自己试过两个模块续航都只有一天左右暂时搁置了。我自己最看重的其实还是记录量。工具再怎么设计真正决定它有没有价值的是半年之后里面有多少条真实数据。所以在做任何新功能之前我都会先问一句这个改动会不会让在水边掏手机记一条变得更麻烦。如果会那就不做。这个判断标准帮我砍掉了不少看着很酷但用不上的东西也是 MiroFish 到现在还能保持两三秒完成一次记录的原因。
返回列表