查看: 132|回复: 3

HarmonyOS 7端侧OCR与跨端同步:字段级LWW实战

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
八月中旬,一个社区生鲜团队启动“冰箱管家”项目:拍冰箱里的食材,自动记录,临期提醒,买菜前查看库存。团队三人,开发者负责端侧,老周兼后端和运维,设计由一位兼职姑娘承担。八月底立项,九月中旬在HarmonyOS 6上跑通第一版,九月底换到HarmonyOS 7(API 26)重做一轮。目标设备包括Mate 60 Pro、MatePad Pro 13.2、问界M9车机、WATCH 5和MateBook Pro,PC端只做查看。项目先确定四条约束:必须离线可用;五块屏数据一致;不许常驻后台;安装包不能大,会员中老年用户看到100MB就不装。第3、4条直接排除了自建同步服务和打包200MB端侧模型的方案,最终落在distributedKVStore与系统Core Vision Kit上,没有打包模型文件。

一、工程骨架与权限:HSP拆分、五端deviceTypes的代价

工程用entry做壳,装路由和首屏;feature_scan、feature_sync、feature_card三个HSP分别负责识别、同步和卡片。选择HSP而不是HAR,是因为HAR会被每个引用模块各打一份。第一版用HAR时,release包里同一份识别代码出现三次,多出6MB。module.json5里deviceTypes一次声明phone、tablet、2in1、wearable、car五个端,差异在代码里用断点区分,不开第二个HAP。代价是每个页面都要在320vp到1440vp之间活着,车机横屏、手表小圆屏、PC大窗口三种极端形态都得考虑。后来用GridRow的320/600/840vp断点做布局,手表端做成独立只读列表;同一个页面硬套五个端,最后五个端都别扭。调试链路也只保留手机、平板、车机三台,五端同时装包改一行同步逻辑成本太高。

权限上踩过坑:DISTRIBUTED_DATASYNC是user_grant,只写进module.json5不够,运行时还要requestPermissionsFromUser,否则同步会失败,而且是静默失败。申请时机放在首次进入同步页,不要放在EntryAbility启动时。代码规范只加了一个code-linter.json5,打开性能相关规则集,重点启用@performance/foreach-args-check,避免规则过多导致新人全点忽略。

二、端侧OCR:把识别从主线程挪走

识别链路是Camera Kit拍照落临时文件,子线程解码成1280宽PixelMap,交给Core Vision Kit做通用文字识别,拿回文本行,再用规则解析成名称和保质期。第一版在拍照回调里直接await textRecognition.recognizeText,用户按下快门后预览画面定住1.2秒,返回键都点不动。Frame Profiler显示主线程有900多毫秒ImageDecode,后面跟着300多毫秒推理,Mate 60 Pro都这样,车机更明显。

第二版改TaskPool。关键细节是不要把PixelMap直接作为参数丢进TaskPool:要么报不可序列化,要么为序列化多花三四十毫秒,比识别本身还贵。正确做法是传文件uri,在子线程里重新解码,多解一次码比跨线程搬4MB像素数据便宜。@Concurrent标记recognizeFromUri,子线程内用fileIo打开uri,createImageSource,按desiredSize 1280×960创建PixelMap,调用textRecognition.recognizeText,最后release PixelMap和source并关闭文件。调用侧用taskpool.execute后要判空,线上遇到子线程抛异常、主线程拿到undefined直接白屏,因此catch里要hilog记录并回退手动输入。

识别跑通后,真正麻烦的是“识别出来的是字,不是数据”。包装上有“巴氏鲜牛奶 950mL / 生产日期 20260912 / 保质期7天”,也有“EXP 2026.09.26”,促销小票还有“会员价 ¥12.90 原价 ¥16.80”。团队写了朴素解析器:用正则找生产日期、生产、MFG和保质期、EXP、有效期;找到日期就归一化成YYYY-MM-DD,找到天数就往前加;两者都找不到就把整块文本交给确认页手填,并用历史最常出现的默认保质期给建议值。词典只有300多条,人工逐条补充,成本低,效果比预期好。团队用240张自拍照片做测试,最终产品取舍很硬:只有印刷包装走自动录入,手写标签和促销小票全部手动,识别结果只作候选词展示。确认页加了一行提示“手写标签建议手动输入,识别率约六成”,投诉消失,手动录入比例从38%涨到55%。能力边界要主动告诉用户,不要指望用户自己摸索。

三、跨端同步:字段级LWW是被整条冲突逼出来的

同步用distributedKVStore,SINGLE_VERSION,一条食材记录一个key。选它是为了省账号体系和鉴权,同一账号同一局域网直接同步,离线写入也能在下次连上后补齐。真正费时间的是冲突。典型场景:晚上8点,用户在客厅用平板把牛奶改成还剩1盒,同时手机离线把同一条记录的保质期从9-19改成9-21。两台设备重连后,KV层整条LWW,要么丢“剩1盒”,要么丢“新保质期”,怎么选都错。内测两周统计137次冲突,其中132次是两个人改的不是同一个字段;只要做字段级合并,96%的冲突不需要用户介入。

因此写入不写整条记录,而写补丁:每个字段带v、t、d,即值、时间戳和设备标识;合并时逐字段比较,时间戳新者胜,时间戳相同用设备标识兜底,保证两端算出同一个结果。ItemRepository初始化KVManager,getKVStore时开启autoSync,监听dataChange的SUBSCRIBE_TYPE_REMOTE,只回补变化的key,不整表重刷。patch方法先读本地补丁,mergePatch后put。删除用墓碑:不真删,标deleted为true,本地保留7天再物理清理。第一版直接delete key,另一台设备离线时把记录重新推上来,“删掉的鸡蛋又回来了”,墓碑加7天窗口后问题消失。剩余4%同字段冲突接受LWW,但详情页底部显示“最后修改:MatePad Pro,2小时前”,让用户知道数据被谁覆盖过,能自己改回来。这一节最大的坑仍在权限:DISTRIBUTED_DATASYNC未运行时申请时,put会成功返回,但数据不出设备;第一版同步回调把异常catch住只打warn,内测第三天出现两台设备数据不一致,查了三小时日志才想起权限没申请。

四、互动卡片:48次刷新额度不能当定时器花

卡片是朋友最看重的功能,理由很朴素:“我妈不会翻App,但她会看桌面。”form_config.json配置expire_card,支持2*4和4*4两种尺寸,window设计宽度720,autoDesignWidth,colorMode自动,updateEnabled为true。第一版把updateDuration设成1,也就是30分钟一刷,还额外挂15分钟定时器去updateForm。上线前压力测试才发现,系统对卡片刷新有配额,超额部分直接丢弃,不会有回调报错;文档按30分钟一个窗口、一天有上限,印象中是48次。日志里看着刷了214次,实际生效可能只有十几次,还抬高功耗,用户看到的是卡片数字有时候不更新。

后来改成两个来源:定点刷新用updateDuration为0加scheduledUpdateTime,只留18:00一个点,设计姑娘定的是“下班回家开冰箱之前”;主动刷新是用户在应用里改数据后立刻推一次,这是用户感知最强、最该花额度的刷新。主应用侧先筛出两天内临期项,用formBindingData.createFormBindingData组装urgentCount、top3和updatedAt,再遍历formId调用formProvider.updateForm;失败则清理已存formId。formId有个关键设计:卡片进程和主应用进程不共享内存,FormExtensionAbility.onAddForm拿到的formId主应用看不见,必须用preferences落盘,onAddForm写、onRemoveForm删。也考虑过dataProxyEnabled数据代理刷新,但它要求数据走DataShare暴露,现有KV结构改造成本不低,而卡片只展示三个字段,主动推足够;团队小的时候,能少一层就少一层。改完后内测期间日均刷新请求从214次降到9次左右,卡片内容过期比例从11.3%降到0.7%。车机卡片另做4*2横版,默认深色,遵守驾驶安全规范,文字不能太小、不能有强动画,只显示“今晚要吃掉3样”,点一下进详情,不做滑动交互。

五、任务接续:只传路由,不传数据快照

任务接续场景很具体:用户在手机上看“牛奶还剩1天”,出门上车,希望车机直接停在那一页。实现是onContinue存状态、onCreate恢复,跨端迁移参数通过want.parameters传。项目只传一个路由字符串shixian.route,数据从KV重新读。第一版把当前页面完整数据快照序列化塞进wantParam,图片路径列表也在里面且base64过,参数体积到100多KB,结果接续后页面白屏,参数被截断,解析抛异常。改成只传route后问题消失,接续耗时从1.4秒降到300多毫秒。onContinue里能做的事很少,不要await网络请求;团队最早想接续时顺便拉最新数据,结果接续直接超时失败。正确姿势是先AGREE过去,页面起来后再触发同步。外部链接用viewData承接,scheme为shixian,host为item,pathPrefix为/detail。意图框架还没跑通,文章先不展开。

六、复盘:五端同时上是错误决定

这一轮最值钱的几条经验是:端侧识别适用场景比宣传窄,只在印刷包装上做自动录入,手写标签和促销小票走手动;跨端一致性不要指望同步框架替你解决,冲突合并必须自己写,而且写到字段级;互动卡片是展示位,不是第二个入口,第一版当入口做白干了两个星期;五端同时上是错误决定,真要做先做手机加只读手表,跑通再铺。离线、安装包小、不常驻后台这几条约束,最终逼出了distributedKVStore加Core Vision Kit、不打包模型的方案;HSP拆分则避免了HAR重复打包。对工具型HarmonyOS应用来说,权限静默失败、卡片刷新配额、TaskPool参数序列化、任务接续参数体积,都是上线前值得专项排查的点。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: HarmonyOS 7端侧OCR与跨端同步:字段级LWW实战

看完很有收获,这帖子全是踩坑后的实话。几个点特别有共鸣:DISTRIBUTED_DATASYNC运行时申请漏掉会静默失败,这个坑太隐蔽了,put返回成功但数据不出设备,排查起来确实要命;TaskPool里不直接传PixelMap而是传uri重新解码,这个取舍很实在,跨线程搬像素的代价比多解一次码高;字段级LWW用v/t/d补丁加设备标识兜底,比整条记录LWW合理太多,墓碑保留7天也解决了“删掉的鸡蛋又回来”这种经典问题。还有把识别能力边界直接写进确认页提示,虽然手动录入比例上去了,但投诉没了,这个产品判断很成熟。 想追问两点:一是PC端只做查看,在distributedKVStore同步里是只订阅不写,还是写权限上做了额外限制?二是卡片部分好像写到“第一版把updateD”就断了,48次刷新额度怎么和临期提醒结合,是纯靠formProvider定时刷新还是配合后台代理提醒?期待后续。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: HarmonyOS 7端侧OCR与跨端同步:字段级LWW实战

这帖子信息量真大,从头到尾都是踩坑踩出来的经验,看着特别过瘾。几个点我特别有共鸣:一是把 PixelMap 直接丢进 TaskPool 那个坑,之前我也以为传对象方便,结果序列化开销比识别还大,后来改传 uri 子线程重解码,虽然多解一次码但整体反而快了,这个取舍很实在。二是字段级 LWW 那段,137 次冲突里 132 次改的不是同一个字段,这个数据一摆出来就很有说服力,整条 LWW 确实是被真实场景逼出来的,墓碑保留 7 天解决“删掉的鸡蛋又回来”也很妙。还有权限那个静默失败,DISTRIBUTED_DATASYNC 不运行时申请 put 还返回成功,这个太阴了,我记下了,以后必须把首次进同步页申请权限当硬规范写进 checklist。HSP 和 HAR 的差异也提醒我了,release 包里同一份代码打三份多出 6MB,对中老年用户安装包敏感的场景确实不能忽视。另外手表端做独立只读列表这个判断我觉得很对,五端硬套一个页面最后哪个端都别扭,能砍就砍。最后卡片那部分好像没写完,48 次刷新额度不能当定时器花,挺想看看后面怎么设计的,期待楼主补完。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: HarmonyOS 7端侧OCR与跨端同步:字段级LWW实战

这篇实战记录很有价值,几个踩坑点写得特别实在。HSP 和 HAR 的包体差异、DISTRIBUTED_DATASYNC 运行时申请静默失败、TaskPool 里传 uri 而不是 PixelMap,这些都是真做过才会总结出来的细节。字段级 LWW 加墓碑和“最后修改来源”提示的处理也很务实,既承认同字段冲突的边界,又给用户可追溯的兜底。OCR 那块把印刷包装和手写标签、促销小票分开,主动提示识别率,比一味追求全自动更符合实际场景。 卡片部分好像还没写完,48 次刷新额度这个约束很关键,期待后续看到怎么做降级和合并刷新。一个小建议:权限申请和同步异常可以统一封装成可观测的初始化检查,内测时把“put 成功但数据不出设备”作为重点用例;补丁合并也可以加端到端回放测试,覆盖离线、乱序、重复推送和墓碑过期。整体看取舍很清楚,读起来很有收获。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-30 02:35 , Processed in 0.026489 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部