ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Harmony os 技术实战|拼豆制图45:上传生成链路如何用状态机替代散落字符串

2026/8/25 13:30:17 拓冰建站 浏览量
Harmony os 技术实战|拼豆制图45:上传生成链路如何用状态机替代散落字符串 上传生成看起来只有两个按钮选择图片、生成图纸。真正运行时却至少包含空闲、打开图库、已选择、转换中、成功和失败六种状态。若只依靠pickedImageUri、一条createStatus字符串和若干布尔值拼判断很容易出现旧预览没有清空、转换中还能重复点击、错误后按钮文案与数据不一致。这篇文章把《拼豆制图》的创建页改造成一个显式状态机让 Harmony os 页面渲染、按钮可用性和异步回调都由同一个状态事实源驱动。一、先列出真实状态而不是控件状态exporttypeCreatePhaseidle|picking|picked|converting|ready|failed;exportinterfaceCreateState{phase:CreatePhase;imageUri:string;result:Pattern|null;message:string;requestId:number;}requestId用来识别过期异步结果。它不是展示字段却是防止“后发请求先完成”覆盖当前选择的关键。二、定义允许发生的状态迁移当前状态事件下一状态idle点击选择pickingpicking选择成功pickedpicking取消idlepicked点击生成convertingconverting转换成功readyconverting转换失败failedready / failed重新选择picking未列出的迁移应被拒绝。例如converting期间再次点击生成不应该再创建第二个任务。三、用一个State保存完整快照StatecreateState:CreateState{phase:idle,imageUri:,result:null,message:请选择一张图片,requestId:0};privatereplaceCreateState(next:CreateState):void{this.createStatenext;}整对象替换能让 ArkUI 明确收到状态变化也避免只改了消息、忘记同步 URI 或结果。四、选择图片时区分取消与失败privateasyncpickImage():Promisevoid{if(this.createState.phasepicking||this.createState.phaseconverting){return;}constrequestIdthis.createState.requestId1;this.replaceCreateState({phase:picking,imageUri:,result:null,message:正在打开系统图库...,requestId});try{consturiawaitImagePickerService.pickSingleImage();if(requestId!this.createState.requestId){return;}if(uri.length0){this.replaceCreateState({phase:idle,imageUri:,result:null,message:未选择图片,requestId});return;}this.replaceCreateState({phase:picked,imageUri:uri,result:null,message:图片已选择可以生成编号图,requestId});}catch(error){this.failCreate(requestId,errorasError);}}取消不是失败页面不应显示红色错误也不应保留上一次图片冒充新选择。五、生成任务要拒绝过期结果privateasyncconvertPickedImage():Promisevoid{if(this.createState.phase!picked){return;}constrequestIdthis.createState.requestId1;consturithis.createState.imageUri;this.replaceCreateState({phase:converting,imageUri:uri,result:null,message:正在读取像素并生成 70×70 图纸...,requestId});try{constoutputawaitImageConvertService.convertFromPickedImage(uri,this.getSelectedPattern());if(requestId!this.createState.requestId){return;}this.replaceCreateState({phase:ready,imageUri:uri,result:output.pattern,message:output.message,requestId});}catch(error){this.failCreate(requestId,errorasError);}}即使用户快速重新选择旧任务完成后也无法覆盖新任务因为它携带的requestId已过期。六、按钮文案与可用性由状态派生privatecreateButtonText():string{if(this.createState.phasepicking){return打开图库中...;}if(this.createState.phaseconverting){return生成中...;}if(this.createState.phaseready){return重新生成;}return生成图纸;}privatecanConvert():boolean{returnthis.createState.phasepicked;}页面不再解析消息文本来判断业务状态。消息只负责展示phase才负责决策。七、失败状态保留可恢复输入privatefailCreate(requestId:number,error:Error):void{if(requestId!this.createState.requestId){return;}this.replaceCreateState({phase:failed,imageUri:this.createState.imageUri,result:null,message:error.message.length0?error.message:生成失败请重新选择图片,requestId});}是否允许直接重试要根据错误类型决定。若 URI 仍可读可以提供“重试”若访问失效则引导重新选择不要一律清空上下文。八、成功后再提交副作用生成结果、最近记录、当前图纸和页面跳转应按固定顺序提交privatecommitGeneratedPattern(pattern:Pattern):void{this.generatedPatternpattern;this.selectedPatternIdpattern.id;this.savePatternRecord(pattern);this.exportStatus;this.activeTabnumbered;}不要在转换开始前写最近记录也不要在结果尚未通过数据不变量校验时跳到编号图页。九、状态机测试比截图更能抓住竞态建议覆盖转换中第二次点击不会发起服务调用。取消选择后imageUri为空。旧requestId的成功回调不会覆盖新状态。失败后按钮可以恢复到重新选择路径。成功后才写入最近记录。十、常见问题与修复问题表现修复用状态文案做判断改文案后逻辑失效使用窄联合phase转换中可重复点击多任务互相覆盖入口状态守卫重新选择仍显示旧结果结果字段未清空进入 picking 时重置结果旧任务晚到覆盖新图无请求身份使用递增requestId复现竞态时不要只快速点击按钮还要人为控制两个异步任务的完成顺序。让旧请求最后返回再确认它因为requestId不一致而被丢弃这种反序用例可以证明状态机真正处理了晚到结果而不只是依赖设备当前恰好较快。十一、验证清单每个界面状态都能映射到唯一phase。未列出的迁移不会执行。取消、失败、成功使用不同语义。重复点击不会启动并发转换。重新选择后旧回调被忽略。记录与跳转只在成功提交时发生。每项验证都应同时观察页面状态和副作用调用次数。例如失败后不仅要看到可重试文案还要确认最近记录没有增加、当前图纸没有替换成功后则应保证记录只写一次避免界面正确但存储已经重复。十二、总结上传生成链路的难点不在按钮数量而在异步事件的先后关系。用一个类型化状态快照承载阶段、输入、结果和请求身份Harmony os 创建页就能把重复点击、过期回调和错误恢复收口到可验证的迁移规则里。标签Harmony os、ArkTS、状态机、异步任务、图片生成