卡片不就是放桌面的小组件吗?那为啥它的更新、交互、生命周期总爱给你“整活儿”?

举报
bug菌 发表于 2025/12/25 14:17:57 2025/12/25
【摘要】 🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8 🤨 前言你有没有过这种“桌面一放卡片,我就开始掉头发”的瞬间:卡片刚...

🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8

🤨 前言

你有没有过这种“桌面一放卡片,我就开始掉头发”的瞬间:卡片刚加上去看着挺美,结果一会儿不更新、一会儿点了没反应、再一会儿生命周期回调压根不进……你盯着桌面,桌面也盯着你:“就这?你还想做原子化服务?😏”
来,今天咱不装斯文——把 原子化服务 & Service Widget(服务卡片) 从“能跑”讲到“跑得稳、更新准、交互顺”,顺便把生命周期那点小脾气也治一治 😄

🧭📌 目录(坑我先替你标出来🕳️)

  • 😄📝 前言:卡片看着小,脾气一点不小
  • 🧩🧠 原子化服务 & 卡片到底是啥:别把它当“缩小版页面”
  • 🧱🗂️ 工程结构:一张卡片通常由哪几块拼起来
  • 🧬⏳ 生命周期:FormExtensionAbility 到底会回调哪些方法
  • 🔄⚡ 动态更新:updateForm / 下次刷新时间 / “别把电量当仇人”
  • 🖱️🎮 交互设计:动态卡片 postCardAction vs 静态卡片 FormLink(别用错!)
  • 🧪👨‍💻 实战代码:做个“天气/待办风格”的可更新卡片(能落地那种)
  • 🧯🚑 常见翻车急救:点不动、刷不动、回调不进——怎么救
  • ✅🎯 总结:卡片写到这个程度,你就能“安心交付”了

😄📝 前言:卡片看着小,脾气一点不小

卡片这玩意儿,最大的误解就是:

“不就是个小 UI 嘛?照页面写不就行了?”

然后你就会被现实教育:

  • 卡片进程可能很快被清理,你刚准备联网,它“啪”一下没了 😅(官方就提到:FormExtensionAbility 创建后 10 秒内无操作会被清理
  • 有些模块在 FormExtensionAbility 里直接不让用,用了可能异常退出(比如粒子能力、音频、相机等)
  • 更新有专用通道,不是你在卡片里 setState 就能“感动系统”刷新的

所以:卡片开发=UI + 数据绑定 + 更新策略 + 交互链路 + 生命周期治理,缺一块都容易翻车。

🧩🧠 原子化服务 & 卡片到底是啥:别把它当“缩小版页面”🫠

在鸿蒙里,Service Widget(服务卡片)是把“信息/动作”前置到桌面或宿主里,让用户少点两步就能完成关键操作。
它的本质更像“被系统托管的 UI 快照 + 受控的交互入口”,而不是一直常驻、随便跑逻辑的页面。

而卡片提供方侧,你核心要掌握的就是 FormExtensionAbility:它负责卡片的创建、刷新、事件回调、销毁等生命周期能力。

🧱🗂️ 工程结构:一张卡片通常由哪几块拼起来 🧩

我用“人话”给你拆一下常见结构(名字可能随模板略有差异,但角色基本一致):

  • 🧠 EntryFormAbility.ets:继承 FormExtensionAbility,处理生命周期(创建/更新/事件/销毁)
  • 🎨 WidgetCard.ets:卡片 UI(ArkTS 声明式 UI)
  • 🧾 form_config.json:卡片配置(尺寸、布局、是否静态等)

你可以把它想成:

UI 负责“长相”,Ability 负责“呼吸和心跳”。

🧬⏳ 生命周期:FormExtensionAbility 到底会回调哪些方法 🧠

官方 API 参考里把一堆回调列得很完整,常用重点是这些:

  • 🆕 onAddForm(want):创建卡片时回调(返回要展示的数据)
  • 🔄 onUpdateForm(formId, wantParams?):更新卡片(你拿到新数据后要主动 updateForm)
  • 📨 onFormEvent(formId, message):卡片触发 message 事件时回调
  • 👀 onChangeFormVisibility(newStatus):可见性变化(注意:有些限制/场景才生效)
  • 🗑️ onRemoveForm(formId):销毁卡片
  • 🧪 onAcquireFormState(want):查询卡片状态(可选重写)

还有两个特别“刺耳”的提醒我建议你贴墙上:

  1. Stage 模型限定:FormExtensionAbility 仅可在 Stage 模型下使用
  2. 能力限制:某些模块不支持在 FormExtensionAbility 引用,否则可能异常退出

🔄⚡ 动态更新:updateForm / 下次刷新时间 / “别把电量当仇人”🔋

卡片更新不是“你想刷就刷”,你要用专用 API:formProvider.updateForm()
同时你还能设置下一次刷新时间:formProvider.setFormNextRefreshTime()

✅ 更新策略我建议分三层(稳得像老干部😄)

  • 🕒 轻量定时:用 setFormNextRefreshTime 告诉系统下次何时刷新(别太频繁)
  • 🧷 用户触发:点“刷新”按钮走 message/call 触发更新(精准又省电)
  • 🚀 重逻辑走 UIAbility:卡片侧只做轻处理,复杂网络/计算交给 UIAbility(尤其是 call 事件)

🖱️🎮 交互设计:动态卡片 postCardAction vs 静态卡片 FormLink(别用错!)😤

这块最容易“用错工具还不自知”。

官方交互概述写得很清楚:

  • 动态卡片:用 postCardAction
  • 静态卡片:用 FormLink
    并且它们都支持 router / message / call 三类事件。

🧠 三种事件怎么选(我给你一套“少踩坑”的直觉)

  • 🧭 router:点卡片跳到应用页面(UIAbility 前台打开)

  • 📨 message:不打开页面,把消息交给 onFormEvent(更适合“轻交互 + 刷新卡片”)

  • ☎️ call:后台拉起 UIAbility 执行某个方法(适合“拉数据/算东西/再回写卡片”)

    • 这里有硬要求:call 事件通常需要传 params.method,并且对 UIAbility 启动模式/后台权限有要求(比如单实例、后台运行权限等)

🧪👨‍💻 实战代码:做个“可更新的小卡片”(带交互、带刷新)✨

下面我给你一套最常用的“卡片三件套”:

  • 卡片 UI 用 @LocalStorageProp 接数据
  • Ability 侧用 formBindingData.createFormBindingData() 产出数据
  • 更新用 formProvider.updateForm() +(可选)setFormNextRefreshTime()

说明:@LocalStorageProp/@LocalStorageLink 从 API 9 开始支持在 ArkTS 卡片里用(元服务从 API 11 开始支持)。

1)🎨 WidgetCard.ets(卡片 UI:显示 + 点击刷新)

// WidgetCard.ets
@Entry
@Component
struct WidgetCard {
  // 卡片数据默认会绑定进 LocalStorage(常见做法就是用装饰器直接读)
  @LocalStorageProp('title') title: string = '⛅ 今日状态'
  @LocalStorageProp('value') value: string = '--'
  @LocalStorageProp('updatedAt') updatedAt: string = ''

  build() {
    Column() {
      Text(`🧩 ${this.title}`).fontSize(16).margin({ bottom: 6 })
      Text(`📌 ${this.value}`).fontSize(22).margin({ bottom: 6 })
      Text(`🕒 ${this.updatedAt || '还没更新呢…😅'}`).fontSize(12).opacity(0.6)

      // ✅ 动态卡片:用 postCardAction
      Button('🔄 点我刷新(message)')
        .margin({ top: 10 })
        .onClick(() => {
          // 动态卡片事件:message / router / call
          postCardAction(this, {
            action: 'message',
            params: { type: 'refresh' }
          })
        })
    }
    .padding(12)
  }
}

postCardAction 专用于卡片内部与提供方交互,支持 router/message/call,且仅在卡片中可调用

2)🧠 EntryFormAbility.ets(生命周期 + 更新)

// EntryFormAbility.ets
import { FormExtensionAbility, formBindingData, formProvider } from '@kit.FormKit'
import { Want } from '@kit.AbilityKit'

function nowString(): string {
  const d = new Date()
  return `${d.toLocaleDateString()} ${d.toLocaleTimeString()}`
}

export default class EntryFormAbility extends FormExtensionAbility {
  // ✅ 创建卡片:返回初始要展示的数据
  onAddForm(want: Want): formBindingData.FormBindingData {
    // want 里通常能拿到 formId、名称、样式等;这些信息建议持久化管理,便于后续更新/删除:contentReference[oaicite:17]{index=17}
    const data = {
      title: '⛅ 今日状态',
      value: '初始化中…',
      updatedAt: nowString()
    }
    return formBindingData.createFormBindingData(data)
  }

  // ✅ 系统/宿主请求更新
  onUpdateForm(formId: string, wantParams?: Record<string, Object>): void {
    const data = {
      title: '⛅ 今日状态',
      value: `更新成功 ✅(param=${JSON.stringify(wantParams ?? {})}`,
      updatedAt: nowString()
    }

    // 关键:拿到新数据后,主动 updateForm
    formProvider.updateForm(formId, formBindingData.createFormBindingData(data))
      .then(() => {
        // 可选:告诉系统“下次多久再刷新”(别太卷,省点电😄)
        // formProvider.setFormNextRefreshTime(formId, Date.now() + 30 * 60 * 1000)
      })
      .catch((e) => {
        console.error(`updateForm failed: ${JSON.stringify(e)}`)
      })
  }

  // ✅ 卡片 message 事件:比如用户点了“刷新”
  onFormEvent(formId: string, message: string): void {
    // message 是事件消息(你可以用 JSON 字符串传)
    const data = {
      title: '⛅ 今日状态',
      value: `来自点击的刷新 🥳(msg=${message}`,
      updatedAt: nowString()
    }
    formProvider.updateForm(formId, formBindingData.createFormBindingData(data))
      .catch(e => console.error(`onFormEvent update failed: ${JSON.stringify(e)}`))
  }

  // ✅ 销毁:清理与该 formId 相关的资源/缓存
  onRemoveForm(formId: string): void {
    console.info(`🗑️ onRemoveForm: ${formId}`)
  }
}

这段能力链路背后对应的官方能力点:

  • FormExtensionAbility 提供创建/更新/事件/销毁回调
  • formProvider 提供 updateFormsetFormNextRefreshTime 等更新能力

3)🧷 静态卡片交互(如果你做的是静态卡片)

静态卡片别用 postCardAction,要用 FormLink,它也支持 router/message/call。

(示意)

FormLink({
  action: 'message',
  params: { type: 'refresh' }
}) {
  Text('🔄 静态卡片刷新').fontSize(14)
}

🧯🚑 常见翻车急救(真的很常见😭)

1)😵 “点刷新没反应”

  • 你做的是 静态卡片 却用了 postCardAction → 换 FormLink
  • message 发了,但 onFormEvent 没处理/没 updateForm → 记得 formProvider.updateForm()

2)🥶 “更新回调进了,但 UI 不变”

  • 卡片 UI 没用 @LocalStorageProp/@LocalStorageLink 绑定对应 key

    • 这类装饰器支持在 ArkTS 卡片里使用(API 9+),元服务(API 11+)
  • 你 updateForm 的 key 和 UI 读的 key 对不上(最气人的“拼写错误”🥲)

3)🫥 “生命周期回调偶发不进/执行很短”

  • FormExtensionAbility 有存活与能力限制,不要在里面干重活;官方还提示它可能被快速清理,并列出多种不支持模块
    ✅ 处理:把重逻辑放到 UIAbility(配合 call),FormExtensionAbility 做“拼装数据 + updateForm”就好

✅🎯 总结:卡片写到这个程度,你就能“安心交付”了 😄

你把下面这套“心法”记住,卡片基本就不会再乱搞你:

  • 🧬 生命周期靠 FormExtensionAbility 管:onAddForm/onUpdateForm/onFormEvent/onRemoveForm(别乱放逻辑)
  • 🔄 更新靠 formProvider.updateForm:想定时就配 setFormNextRefreshTime,但别太频繁
  • 🎮 交互分清楚:动态卡片 postCardAction,静态卡片 FormLink,都支持 router/message/call,但各有适用场景
  • 🧠 数据绑定用 @LocalStorageProp/@LocalStorageLink:卡片里读数据最省心(API 9+ 支持卡片,API 11+ 支持元服务)

🧧福利赠与你🧧

  无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」专栏(全网一个名),bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。

  最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

  同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。

✨️ Who am I?

我是bug菌(全网一个名),CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主/价值贡献奖,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。