三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

卡片刷新三板斧:定时、定点、主动请求——搞清楚才不会乱

卡片刷新三板斧:定时、定点、主动请求——搞清楚才不会乱

文章目录

      • 先搞明白谁在控制刷新
      • 方式一:定时刷新(updateDuration)
      • 方式二:定点刷新(scheduledUpdateTime)
      • 方式三:主动请求刷新(formHost.requestForm)
      • 三种方式对比
      • 卡片 UI 接收数据
      • 整体数据流图
      • 常见问题
      • 写在最后

写卡片刷新功能踩了不少坑,最大的问题是:刷新方式有三种,文档描述很相似,一不小心配错了,卡片数据死活不更新,死活不知道原因。今天把这三种方式彻底讲清楚。

先搞明白谁在控制刷新

刷新卡片数据的流程是这样的:

  1. 触发刷新(可能是定时器、定点时间、宿主应用按钮)
  2. 系统回调FormExtensionAbility.onUpdateForm(formId)
  3. 你在里面构造新数据,调formProvider.updateForm(formId, newData)推送
  4. 卡片 UI 收到LocalStorage更新,自动重渲染

三种刷新方式的区别只在第 1 步——触发方式不同,后面都一样。

方式一:定时刷新(updateDuration)

最简单,设置一个刷新间隔,系统自动按时来敲门。

form_config.json里配置:

{"forms":[{"name":"widget","updateEnabled":true,// 必须是 true,否则全部刷新功能都不生效"updateDuration":1,// 单位:30分钟,填 1 = 每30分钟刷一次"scheduledUpdateTime":""// 定时刷新时此字段留空或不填}]}

注意updateDuration的单位比较坑,不是分钟,也不是秒,是"30分钟的倍数"

updateDuration 值实际刷新间隔
130 分钟
260 分钟(1小时)
4824 小时

系统最短刷新间隔是 30 分钟,不能更短了,这是平台限制。

FormAbility 里处理刷新:

// entry/src/main/ets/entryformability/EntryFormAbility.etsimport{formBindingData,FormExtensionAbility,formProvider}from'@kit.FormKit';import{Want}from'@kit.AbilityKit';import{BusinessError}from'@kit.BasicServicesKit';exportdefaultclassEntryFormAbilityextendsFormExtensionAbility{onAddForm(want:Want):formBindingData.FormBindingData{// 卡片创建时返回初始数据constformData:Record<string,string>={};returnformBindingData.createFormBindingData(formData);}onUpdateForm(formId:string):void{// 系统按照 updateDuration 的间隔自动调用这个方法// 在这里准备新数据,然后推送给卡片classFormDataClass{title:string='Title Update.';// 对应卡片 UI 里的 title 绑定detail:string='Description update success.';// 对应卡片 UI 里的 detail 绑定}constformData=newFormDataClass();constformInfo=formBindingData.createFormBindingData(formData);// 关键:用 formProvider.updateForm 推送数据formProvider.updateForm(formId,formInfo).then(()=>{console.info('定时刷新成功');}).catch((error:BusinessError)=>{console.error(`定时刷新失败:${JSON.stringify(error)}`);});}}

方式二:定点刷新(scheduledUpdateTime)

在每天固定的时间点刷一次,比如每天上午 10:30 更新。

修改form_config.json

{"forms":[{"name":"widget","updateEnabled":true,"updateDuration":0,// 定点刷新时,updateDuration 要设为 0"scheduledUpdateTime":"10:30"// 每天 10:30 刷新,24小时制}]}

注意:updateDurationscheduledUpdateTime是互斥的。想用定点刷新,updateDuration必须设为0

onUpdateForm的代码和定时刷新完全一样,只是触发的时间点不同。

方式三:主动请求刷新(formHost.requestForm)

前两种是系统主动来找你,这种是宿主应用(卡片使用方)主动要求刷新。

典型场景:用户在应用里点了"刷新"按钮,你希望卡片立马更新数据。

宿主应用(FormUpdateDemo)的 Index 页面:

// entry/src/main/ets/pages/Index.etsimport{formHost}from'@kit.FormKit';import{BusinessError}from'@kit.BasicServicesKit';import{hilog}from'@kit.PerformanceAnalysisKit';constDOMAIN_NUMBER:number=0xFF00;letstorage=newLocalStorage();@Entry(storage)@Componentstruct Index{@StateformId:string='0';// 这个 ID 要从 FormComponent 的 onAcquired 回调里取build(){Column(){// FormComponent 展示卡片,同时获取 formIdFormComponent({id:0,name:'widget',bundle:'com.samples.formupdatedemo',ability:'EntryFormAbility',module:'entry',dimension:2,temporary:false,}).size({width:200,height:200}).borderColor(Color.Black).borderRadius(10).borderWidth(1)// 卡片成功加载后,拿到 formId.onAcquired((form:FormCallbackInfo)=>{hilog.info(DOMAIN_NUMBER,'testTag',`onAcquired:${JSON.stringify(form)}`);this.formId=form.id.toString();// 保存 formId,后续刷新用}).onRouter(()=>{hilog.info(DOMAIN_NUMBER,'testTag','onRouter');}).onError((error)=>{hilog.error(DOMAIN_NUMBER,'testTag',`onError:${JSON.stringify(error)}`);})Text(`当前 formId:${this.formId}`).fontSize(14).margin(10)// 点击按钮,主动请求刷新Button('刷新卡片').onClick(()=>{if(this.formId!=='0'){// formHost.requestForm 触发 onUpdateForm 回调formHost.requestForm(this.formId).then(()=>{hilog.info(DOMAIN_NUMBER,'testTag','主动请求刷新成功');}).catch((error:BusinessError)=>{hilog.error(DOMAIN_NUMBER,'testTag',`刷新失败:${error?.code}${error?.message}`);});}}).margin(5).width('50%')}.width('100%').height('100%')}}

流程重点:先从FormComponent.onAcquired拿到formId,再用formHost.requestForm(formId)触发刷新。如果没拿到formId就调用,直接报错。

三种方式对比

方式触发者配置位置最小间隔适用场景
定时刷新系统form_config.jsonupdateDuration30分钟定期更新天气、新闻等
定点刷新系统form_config.jsonscheduledUpdateTime每天一次每日早报、定时提醒
主动请求宿主应用代码调用无限制用户手动触发、实时数据

卡片 UI 接收数据

三种方式在FormAbility里最终都调用formProvider.updateForm,卡片 UI 通过@LocalStorageProp接收更新后的数据:

// entry/src/main/ets/widget/pages/WidgetCard.etsletstorage:LocalStorage=newLocalStorage();@Entry(storage)@Componentstruct WidgetCard{// 这两个字段名要和 FormAbility 里 FormDataClass 的字段名完全一致@LocalStorageProp('title')title:string='加载中...';@LocalStorageProp('detail')detail:string='';build(){Column(){Text(this.title).fontSize(16).fontColor('#FFFFFF').margin({top:'8%',left:'10%'})Text(this.detail).fontSize(12).fontColor('#FFFFFF').opacity(0.6).margin({top:'5%',left:'10%'})}.width('100%').height('100%')}}

整体数据流图

常见问题

Q:updateEnabled设成false了,但是requestForm还能用吗?

不能用。updateEnabled是总开关,关掉之后所有刷新方式(包括主动请求)都会被拦截,onUpdateForm不会被调用。

Q:定时刷新和定点刷新能同时配吗?

不能。两个配置互斥,同时填了系统只会用其中一种(通常是scheduledUpdateTime优先)。

Q:卡片在后台不可见时还会刷新吗?

不会,系统为了省电,不可见的卡片不会触发onUpdateForm。等卡片重新可见时,会触发一次刷新。

写在最后

这三种方式各有用途,别混用。想让卡片自动更新天气、股价用定时刷新;想让卡片每天固定时间更新日报用定点刷新;想让用户点按钮立刻刷新就用formHost.requestForm

选对了方式,卡片就省电又好用。

← 返回列表