HarmonyOS开发:卡片生命周期——创建、更新、删除与定时刷新
HarmonyOS开发:卡片生命周期——创建、更新、删除与定时刷新
📌 核心要点:卡片生命周期由系统驱动,onAddForm/onUpdateForm/onRemoveForm三个回调构成了卡片从生到死的完整链路,理解每个阶段的时序和约束是写出稳定卡片的前提。
背景与动机
你有没有遇到过这种诡异的情况:卡片添加上去好好的,过一会儿数据就不对了?或者用户删了卡片,你的后台还在拼命更新一个不存在的formId?
这些问题的根源都是——没搞清楚卡片的生命周期。
卡片不是普通UI组件,它的生命周期不由你的代码控制,而是由系统调度。用户什么时候添加、系统什么时候触发更新、用户什么时候删除,你只能被动响应。但"被动响应"不等于"随便写",每个回调都有严格的执行约束。超时了数据就丢了,异常了卡片就白屏了。
搞清楚卡片生命周期,你才能知道:
- 在哪个回调里做什么事最合适
- 哪些操作不能在回调里做
- 怎么在生命周期切换时保持数据一致性
- 定时刷新到底是怎么触发的
核心原理
卡片完整生命周期
flowchart TB
A[用户长按桌面<br/>选择添加卡片] --> B[系统创建卡片实例]
B --> C[onAddForm回调]
C --> D[返回FormBindingData]
D --> E[卡片渲染显示]
E --> F{等待更新触发}
F -->|定时触发| G[onUpdateForm回调]
F -->|应用主动更新| H[formProvider.updateForm]
F -->|卡片可见性变化| I[onVisibilityChange]
G --> J[返回新FormBindingData]
J --> E
H --> K[直接更新卡片数据]
K --> E
I --> L[暂停/恢复更新]
L --> F
E --> M[用户长按卡片<br/>选择移除]
M --> N[onRemoveForm回调]
N --> O[清理资源]
O --> P[卡片从桌面消失]
E --> Q[应用被卸载]
Q --> N
classDef userAction fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20
classDef callback fill:#E3F2FD,stroke:#1565C0,color:#0D47A1
classDef data fill:#FFF3E0,stroke:#E65100,color:#BF360C
classDef endState fill:#FCE4EC,stroke:#C62828,color:#B71C1C
class A,M,Q userAction
class C,G,I,N callback
class D,J,K,H data
class E,P,O endState
这张图涵盖了卡片从生到死的所有路径。但有几个细节需要特别说明:
- onAddForm只执行一次——用户添加卡片时触发,之后不会再触发
- onUpdateForm可以执行多次——每次系统触发更新都会调用
- onRemoveForm是最终回调——执行完这个,卡片就彻底没了
- formProvider.updateForm不触发onUpdateForm——它是直接更新,绕过FormAbility
回调执行时序
sequenceDiagram
participant User as 用户
participant Desktop as 桌面
participant FMS as FormManagerService
participant FA as FormAbility
participant Cache as 数据缓存
Note over User,Cache: 阶段一:创建
User->>Desktop: 添加卡片
Desktop->>FMS: 请求创建
FMS->>FA: onAddForm(want)
FA->>Cache: 读取缓存数据
Cache-->>FA: 返回缓存
FA-->>FMS: FormBindingData
FMS->>Desktop: 渲染卡片
Note over User,Cache: 阶段二:定时更新
loop 每隔N小时
FMS->>FA: onUpdateForm(formId)
FA->>Cache: 读取最新数据
Cache-->>FA: 返回数据
FA-->>FMS: 新FormBindingData
FMS->>Desktop: 刷新卡片
end
Note over User,Cache: 阶段三:删除
User->>Desktop: 移除卡片
Desktop->>FMS: 请求删除
FMS->>FA: onRemoveForm(formId)
FA->>Cache: 清理该卡片缓存
FA-->>FMS: 完成
FMS->>Desktop: 移除卡片UI
注意一个关键细节:formProvider.updateForm和onUpdateForm是两条独立的更新路径。
onUpdateForm:系统定时触发,你在回调里返回新数据formProvider.updateForm:你主动调用,直接推送数据给卡片
两者不冲突,可以同时使用。但要注意数据一致性——两条路径推的数据不能矛盾。
定时刷新的触发机制
定时刷新不是简单的"每隔N小时执行一次"。它的触发逻辑是这样的:
- 系统读取form_config.json中的
scheduledUpdateTime和updateDuration - 在
scheduledUpdateTime时刻,系统检查是否满足更新条件(电量、网络、用户活跃度) - 满足条件则触发
onUpdateForm,不满足则延迟 - 如果
updateDuration > 0,系统会在后续时间点继续检查和触发
flowchart LR
A[scheduledUpdateTime<br/>07:00] --> B{系统检查条件}
B -->|电量>20%<br/>网络可用<br/>用户活跃| C[触发onUpdateForm]
B -->|条件不满足| D[延迟触发<br/>可能07:15或更晚]
C --> E[等待下一个周期]
D --> E
E --> F{updateDuration}
F -->|=0| G[仅此一次<br/>明天07:00再触发]
F -->|=1| H[1小时后再次检查<br/>08:00]
F -->|=2| I[2小时后再次检查<br/>09:00]
classDef time fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20
classDef check fill:#FFF3E0,stroke:#E65100,color:#BF360C
classDef action fill:#E3F2FD,stroke:#1565C0,color:#0D47A1
class A time
class B check
class C,D action
class G,H,I action
代码实战
基础用法:完整的生命周期管理
一个健壮的FormAbility必须正确处理每个生命周期回调,并维护卡片的状态。
// LifecycleFormAbility.ets
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
// 卡片状态管理
interface CardState {
formId: string
formName: string
dimension: number
createdAt: number
lastUpdated: number
data: Record<string, string>
}
class CardStateManager {
private cards: Map<string, CardState> = new Map()
private static instance: CardStateManager
static getInstance(): CardStateManager {
if (!CardStateManager.instance) {
CardStateManager.instance = new CardStateManager()
}
return CardStateManager.instance
}
// 卡片创建
addCard(formId: string, formName: string, dimension: number, data: Record<string, string>): void {
this.cards.set(formId, {
formId,
formName,
dimension,
createdAt: Date.now(),
lastUpdated: Date.now(),
data
})
hilog.info(0x0000, 'CardState', `卡片创建: ${formId}, 类型: ${formName}, 尺寸: ${dimension}`)
}
// 卡片更新
updateCard(formId: string, data: Record<string, string>): void {
const card = this.cards.get(formId)
if (card) {
card.data = { ...card.data, ...data }
card.lastUpdated = Date.now()
}
}
// 卡片删除
removeCard(formId: string): CardState | undefined {
const card = this.cards.get(formId)
this.cards.delete(formId)
hilog.info(0x0000, 'CardState', `卡片删除: ${formId}`)
return card
}
// 获取卡片信息
getCard(formId: string): CardState | undefined {
return this.cards.get(formId)
}
// 获取所有卡片ID
getAllFormIds(): string[] {
return Array.from(this.cards.keys())
}
// 获取指定类型的卡片
getCardsByType(formName: string): CardState[] {
const result: CardState[] = []
this.cards.forEach(card => {
if (card.formName === formName) result.push(card)
})
return result
}
}
const stateManager = CardStateManager.getInstance()
export default class LifecycleFormAbility extends FormExtensionAbility {
// ===== 创建阶段 =====
onAddForm(want: Want): formBindingData.FormBindingData {
const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string
const formName = want.parameters?.['ohos.extra.param.key.form_name'] as string
const dimension = want.parameters?.['ohos.extra.param.key.form_dimension'] as number
hilog.info(0x0000, 'Lifecycle', `onAddForm: id=${formId}, name=${formName}, dim=${dimension}`)
// 获取初始数据
const initialData = this.getInitialData(formName)
// 注册卡片状态
stateManager.addCard(formId, formName, dimension, initialData)
return formBindingData.createFormBindingData(initialData)
}
// ===== 更新阶段 =====
onUpdateForm(formId: string): formBindingData.FormBindingData {
hilog.info(0x0000, 'Lifecycle', `onUpdateForm: ${formId}`)
const card = stateManager.getCard(formId)
if (!card) {
// 卡片状态丢失,返回空数据
hilog.warn(0x0000, 'Lifecycle', `卡片状态不存在: ${formId}`)
return formBindingData.createFormBindingData({ error: '状态丢失' })
}
// 获取最新数据
const newData = this.getLatestData(card.formName)
// 更新状态
stateManager.updateCard(formId, newData)
return formBindingData.createFormBindingData(newData)
}
// ===== 删除阶段 =====
onRemoveForm(formId: string): void {
hilog.info(0x0000, 'Lifecycle', `onRemoveForm: ${formId}`)
const card = stateManager.removeCard(formId)
if (card) {
// 清理该卡片相关的资源
this.cleanupCardResources(card)
}
}
// ===== 事件处理 =====
onFormEvent(formId: string, message: string): void {
hilog.info(0x0000, 'Lifecycle', `onFormEvent: ${formId}, msg=${message}`)
const card = stateManager.getCard(formId)
if (!card) return
// 处理卡片发来的消息
const msg = JSON.parse(message)
this.handleCardMessage(formId, card.formName, msg)
}
// ===== 可见性变化(6.0新增) =====
onVisibilityChange(formIds: string[], isVisible: boolean): void {
hilog.info(0x0000, 'Lifecycle', `onVisibilityChange: ${formIds}, visible=${isVisible}`)
formIds.forEach(formId => {
if (isVisible) {
this.resumeCardUpdates(formId)
} else {
this.pauseCardUpdates(formId)
}
})
}
// ===== 辅助方法 =====
private getInitialData(formName: string): Record<string, string> {
switch (formName) {
case 'weatherCard':
return { city: '北京', temperature: '--°', weather: '加载中' }
case 'todoCard':
return { count: '0', latest: '暂无待办' }
default:
return { title: '加载中' }
}
}
private getLatestData(formName: string): Record<string, string> {
// 从数据源获取最新数据
switch (formName) {
case 'weatherCard':
return this.fetchWeatherData()
case 'todoCard':
return this.fetchTodoData()
default:
return { title: '已更新' }
}
}
private fetchWeatherData(): Record<string, string> {
return {
city: '北京',
temperature: '26°',
weather: '晴',
updateTime: new Date().toLocaleTimeString()
}
}
private fetchTodoData(): Record<string, string> {
return {
count: '3',
latest: '完成周报',
updateTime: new Date().toLocaleTimeString()
}
}
private cleanupCardResources(card: CardState): void {
// 取消该卡片相关的定时器
// 清理该卡片相关的缓存
// 通知数据源停止为该卡片提供数据
hilog.info(0x0000, 'Lifecycle', `清理卡片资源: ${card.formId}`)
}
private handleCardMessage(formId: string, formName: string, msg: Record<string, string>): void {
// 根据消息类型处理
hilog.info(0x0000, 'Lifecycle', `处理卡片消息: ${JSON.stringify(msg)}`)
}
private resumeCardUpdates(formId: string): void {
// 卡片变为可见,恢复更新
hilog.info(0x0000, 'Lifecycle', `恢复卡片更新: ${formId}`)
}
private pauseCardUpdates(formId: string): void {
// 卡片变为不可见,暂停更新
hilog.info(0x0000, 'Lifecycle', `暂停卡片更新: ${formId}`)
}
}
进阶用法:定时刷新与数据同步
定时刷新的关键是理解"系统触发"和"应用主动"两种更新方式的配合。最佳实践是:定时刷新兜底,主动更新补位。
// SyncFormAbility.ets —— 带数据同步的卡片
import { formBindingData, FormExtensionAbility, formProvider } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { distributedKVStore } from '@kit.ArkData'
// 分布式键值存储,用于UIAbility和FormAbility数据同步
let kvStore: distributedKVStore.SingleKVStore | null = null
// 卡片更新队列——解决5分钟限制
interface PendingUpdate {
formId: string
data: Record<string, string>
scheduledTime: number // 计划执行时间
}
class UpdateScheduler {
private pendingQueue: PendingUpdate[] = []
private timer: number | null = null
private readonly MIN_INTERVAL = 5 * 60 * 1000 // 5分钟
// 添加待更新任务
scheduleUpdate(formId: string, data: Record<string, string>): void {
const lastUpdate = this.getLastUpdateTime(formId)
const nextAvailableTime = lastUpdate + this.MIN_INTERVAL
if (Date.now() >= nextAvailableTime) {
// 可以立即更新
this.executeUpdate(formId, data)
} else {
// 需要延迟更新
this.pendingQueue.push({
formId,
data,
scheduledTime: nextAvailableTime
})
this.scheduleNextExecution()
}
}
// 执行更新
private executeUpdate(formId: string, data: Record<string, string>): void {
const bindingData = formBindingData.createFormBindingData(data)
formProvider.updateForm(formId, bindingData)
.then(() => {
this.recordUpdateTime(formId)
hilog.info(0x0000, 'UpdateScheduler', `更新成功: ${formId}`)
})
.catch((err: Error) => {
hilog.error(0x0000, 'UpdateScheduler', `更新失败: ${err.message}`)
})
}
// 调度下一次执行
private scheduleNextExecution(): void {
if (this.timer) return
// 找到最近的待执行任务
const nextTask = this.pendingQueue
.sort((a, b) => a.scheduledTime - b.scheduledTime)[0]
if (!nextTask) return
const delay = Math.max(nextTask.scheduledTime - Date.now(), 0)
this.timer = setTimeout(() => {
this.processQueue()
this.timer = null
}, delay)
}
// 处理队列
private processQueue(): void {
const now = Date.now()
const ready: PendingUpdate[] = []
const pending: PendingUpdate[] = []
this.pendingQueue.forEach(task => {
if (task.scheduledTime <= now) {
ready.push(task)
} else {
pending.push(task)
}
})
this.pendingQueue = pending
// 合并同一formId的更新(只保留最新的)
const merged = new Map<string, Record<string, string>>()
ready.forEach(task => {
merged.set(task.formId, { ...(merged.get(task.formId) || {}), ...task.data })
})
// 执行更新
merged.forEach((data, formId) => {
this.executeUpdate(formId, data)
})
// 调度下一轮
if (this.pendingQueue.length > 0) {
this.scheduleNextExecution()
}
}
private updateTimes: Map<string, number> = new Map()
private getLastUpdateTime(formId: string): number {
return this.updateTimes.get(formId) || 0
}
private recordUpdateTime(formId: string): void {
this.updateTimes.set(formId, Date.now())
}
}
const scheduler = new UpdateScheduler()
export default class SyncFormAbility extends FormExtensionAbility {
onAddForm(want: Want): formBindingData.FormBindingData {
const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string
// 从KV存储读取数据
const data = this.readFromKVStore(formId)
return formBindingData.createFormBindingData(data)
}
onUpdateForm(formId: string): formBindingData.FormBindingData {
// 定时更新:从KV存储读取最新数据
const data = this.readFromKVStore(formId)
return formBindingData.createFormBindingData(data)
}
onRemoveForm(formId: string): void {
// 清理KV存储中该卡片的数据
this.removeFromKVStore(formId)
}
// 从KV存储读取数据
private readFromKVStore(formId: string): Record<string, string> {
if (!kvStore) {
return { title: '数据加载中' }
}
try {
const value = kvStore.get(formId)
if (value) {
return JSON.parse(value as string)
}
} catch (err) {
hilog.error(0x0000, 'SyncForm', `KV读取失败: ${err}`)
}
return { title: '暂无数据' }
}
private removeFromKVStore(formId: string): void {
if (!kvStore) return
try {
kvStore.delete(formId)
} catch (err) {
hilog.error(0x0000, 'SyncForm', `KV删除失败: ${err}`)
}
}
}
完整示例:带错误恢复的健壮卡片
生产环境的卡片必须能处理各种异常情况——网络超时、数据为空、卡片状态丢失。下面是一个带完整错误恢复机制的FormAbility:
// RobustFormAbility.ets
import { formBindingData, FormExtensionAbility, formProvider } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
// 错误恢复策略
enum RecoveryStrategy {
USE_CACHE, // 使用缓存
USE_DEFAULT, // 使用默认值
SHOW_ERROR // 显示错误信息
}
// 卡片数据包装器——自动处理错误
class CardDataWrapper {
private formId: string
private formName: string
constructor(formId: string, formName: string) {
this.formId = formId
this.formName = formName
}
// 安全获取数据
async safeFetch(fetchFn: () => Promise<Record<string, string>>): Promise<Record<string, string>> {
try {
const data = await fetchFn()
// 验证数据完整性
if (!this.validateData(data)) {
hilog.warn(0x0000, 'CardData', `数据验证失败: ${this.formId}`)
return this.getRecoveryData(RecoveryStrategy.USE_CACHE)
}
// 缓存数据
this.cacheData(data)
return data
} catch (err) {
hilog.error(0x0000, 'CardData', `数据获取失败: ${this.formId}, ${err}`)
return this.getRecoveryData(RecoveryStrategy.USE_CACHE)
}
}
// 验证数据
private validateData(data: Record<string, string>): boolean {
if (!data || Object.keys(data).length === 0) return false
// 检查关键字段
switch (this.formName) {
case 'weatherCard':
return !!data.city && !!data.temperature
case 'todoCard':
return data.count !== undefined
default:
return true
}
}
// 缓存数据
private cacheData(data: Record<string, string>): void {
try {
// 使用Preferences缓存
const key = `card_cache_${this.formId}`
// 实际项目用Preferences存储
hilog.info(0x0000, 'CardData', `缓存数据: ${key}`)
} catch (err) {
hilog.error(0x0000, 'CardData', `缓存失败: ${err}`)
}
}
// 获取恢复数据
private getRecoveryData(strategy: RecoveryStrategy): Record<string, string> {
switch (strategy) {
case RecoveryStrategy.USE_CACHE:
// 尝试读取缓存
hilog.info(0x0000, 'CardData', `使用缓存恢复: ${this.formId}`)
return this.readCache() || this.getDefaultData()
case RecoveryStrategy.USE_DEFAULT:
return this.getDefaultData()
case RecoveryStrategy.SHOW_ERROR:
return {
title: '数据加载失败',
subtitle: '请检查网络后重试',
errorFlag: 'true'
}
}
}
private readCache(): Record<string, string> | null {
// 从缓存读取
return null
}
private getDefaultData(): Record<string, string> {
switch (this.formName) {
case 'weatherCard':
return { city: '--', temperature: '--°', weather: '--', icon: 'default' }
case 'todoCard':
return { count: '0', latest: '暂无待办' }
default:
return { title: '加载中' }
}
}
}
// 活跃卡片追踪
let activeFormIds: Set<string> = new Set()
let cardWrappers: Map<string, CardDataWrapper> = new Map()
export default class RobustFormAbility extends FormExtensionAbility {
onAddForm(want: Want): formBindingData.FormBindingData {
const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string
const formName = want.parameters?.['ohos.extra.param.key.form_name'] as string
hilog.info(0x0000, 'RobustForm', `创建卡片: ${formId} (${formName})`)
// 注册卡片
activeFormIds.add(formId)
cardWrappers.set(formId, new CardDataWrapper(formId, formName))
// 立即返回默认数据
const defaultData = this.getDefaultData(formName)
// 异步获取真实数据
const wrapper = cardWrappers.get(formId)
if (wrapper) {
wrapper.safeFetch(() => this.fetchData(formName))
.then(data => {
// 数据获取成功,主动更新
const bindingData = formBindingData.createFormBindingData(data)
formProvider.updateForm(formId, bindingData)
})
}
return formBindingData.createFormBindingData(defaultData)
}
onUpdateForm(formId: string): formBindingData.FormBindingData {
hilog.info(0x0000, 'RobustForm', `更新卡片: ${formId}`)
const wrapper = cardWrappers.get(formId)
if (!wrapper) {
// 卡片状态丢失,尝试恢复
hilog.warn(0x0000, 'RobustForm', `状态丢失,使用默认数据: ${formId}`)
return formBindingData.createFormBindingData(this.getDefaultData('unknown'))
}
// 同步返回缓存数据(onUpdateForm不能异步)
const cachedData = this.getCachedData(formId)
if (cachedData) {
return formBindingData.createFormBindingData(cachedData)
}
return formBindingData.createFormBindingData(this.getDefaultData('unknown'))
}
onRemoveForm(formId: string): void {
hilog.info(0x0000, 'RobustForm', `删除卡片: ${formId}`)
activeFormIds.delete(formId)
cardWrappers.delete(formId)
this.clearCache(formId)
}
private async fetchData(formName: string): Promise<Record<string, string>> {
// 模拟网络请求
return new Promise((resolve) => {
setTimeout(() => {
switch (formName) {
case 'weatherCard':
resolve({
city: '北京',
temperature: '26°',
weather: '晴',
updateTime: new Date().toLocaleTimeString()
})
break
case 'todoCard':
resolve({
count: '5',
latest: '完成周报',
updateTime: new Date().toLocaleTimeString()
})
break
default:
resolve({ title: '已更新' })
}
}, 500)
})
}
private getDefaultData(formName: string): Record<string, string> {
switch (formName) {
case 'weatherCard':
return { city: '--', temperature: '--°', weather: '加载中' }
case 'todoCard':
return { count: '--', latest: '加载中' }
default:
return { title: '加载中' }
}
}
private getCachedData(formId: string): Record<string, string> | null {
// 从缓存读取
return null
}
private clearCache(formId: string): void {
// 清理缓存
}
}
踩坑与注意事项
坑1:onAddForm回调有执行时间限制
FormAbility的回调有执行时间限制(通常5秒)。如果你在onAddForm里做耗时操作(同步网络请求、大量计算),超时后系统会丢弃返回的数据,卡片显示空白。
// ❌ 错误:同步耗时操作
onAddForm(want: Want): formBindingData.FormBindingData {
const data = this.syncFetchFromNetwork() // 可能超时!
return formBindingData.createFormBindingData(data)
}
// ✅ 正确:先返回默认数据,异步更新
onAddForm(want: Want): formBindingData.FormBindingData {
const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string
// 立即返回默认数据
const defaultData = { title: '加载中' }
// 异步获取真实数据
this.asyncFetch().then(data => {
formProvider.updateForm(formId, formBindingData.createFormBindingData(data))
})
return formBindingData.createFormBindingData(defaultData)
}
坑2:onUpdateForm里不能做异步操作
onUpdateForm是同步回调,必须立即返回FormBindingData。你不能在里面用await或返回Promise。
// ❌ 错误:异步返回
async onUpdateForm(formId: string): Promise<formBindingData.FormBindingData> {
const data = await this.fetchData() // 编译可能通过,但运行时行为不确定
return formBindingData.createFormBindingData(data)
}
// ✅ 正确:同步返回缓存数据
onUpdateForm(formId: string): formBindingData.FormBindingData {
const cached = this.readFromCache(formId)
return formBindingData.createFormBindingData(cached)
}
坑3:FormAbility可能在onCreate后立即被销毁
FormAbility的生命周期很短——系统调用完回调后,可能立即销毁FormAbility实例。这意味着你不能依赖FormAbility的成员变量来保存状态。
// ❌ 错误:依赖成员变量
export default class BadFormAbility extends FormExtensionAbility {
private cardCount: number = 0 // 可能丢失!
onAddForm(want: Want): formBindingData.FormBindingData {
this.cardCount++ // 不可靠
return formBindingData.createFormBindingData({ count: this.cardCount.toString() })
}
}
// ✅ 正确:使用外部存储
let globalCardCount: number = 0 // 模块级变量,相对可靠
export default class GoodFormAbility extends FormExtensionAbility {
onAddForm(want: Want): formBindingData.FormBindingData {
globalCardCount++
return formBindingData.createFormBindingData({ count: globalCardCount.toString() })
}
}
更可靠的方式是使用Preferences或KV存储来持久化状态。
坑4:卡片ID可能重复使用
系统可能会复用已删除卡片的formId。如果你在onRemoveForm里清理了缓存,但之后系统又用同一个formId创建了新卡片,你的缓存就没了。
解决方案:onRemoveForm里不要立即删除缓存,而是标记为"已删除"。下次onAddForm时检查是否有同formId的缓存,有则复用。
坑5:定时刷新在设备休眠时不触发
设备休眠时,定时刷新不会触发。设备唤醒后,系统会补发一次更新。但如果你依赖"每N小时更新一次"来计算数据,可能会出现时间跳跃。
onUpdateForm(formId: string): formBindingData.FormBindingData {
// ❌ 错误:假设更新间隔固定
const hoursSinceLastUpdate = 1 // 不一定!设备休眠期间不会更新
// ✅ 正确:使用实际时间计算
const now = Date.now()
const lastUpdate = this.getLastUpdateTime(formId)
const actualHours = (now - lastUpdate) / (1000 * 60 * 60)
return formBindingData.createFormBindingData(this.calculateData(actualHours))
}
HarmonyOS 6适配说明
-
onVisibilityChange回调:6.0新增了
onVisibilityChange回调,当卡片从桌面滑出/滑入可视区域时触发。你可以利用这个回调暂停不可见卡片的更新,节省电量和CPU。 -
onAcquireFormState回调:6.0新增了
onAcquireFormState回调,系统在添加卡片前会先调用这个回调查询卡片状态。你可以在这里返回卡片是否可用、是否需要网络等信息,系统据此决定是否允许添加。 -
生命周期回调超时时间调整:6.0将
onAddForm和onUpdateForm的超时时间从5秒延长到了10秒,给了开发者更多操作空间。但仍然建议尽快返回,不要做耗时操作。 -
卡片状态持久化增强:6.0新增了
formProvider.saveFormState()API,允许你在FormAbility被销毁前保存卡片状态。下次FormAbility被创建时,可以通过formProvider.restoreFormState()恢复。
// 6.0新增的状态保存/恢复
onAddForm(want: Want): formBindingData.FormBindingData {
// 尝试恢复之前的状态
const savedState = formProvider.restoreFormState(this.context, formId)
if (savedState) {
return formBindingData.createFormBindingData(savedState)
}
return formBindingData.createFormBindingData(this.getDefaultData())
}
onRemoveForm(formId: string): void {
// 保存卡片状态(以防formId被复用)
formProvider.saveFormState(this.context, formId, this.getCurrentData(formId))
}
总结
卡片生命周期是系统驱动的,你只能被动响应。但"被动"不等于"被动挨打"——理解每个回调的时序、约束、最佳实践,你就能写出健壮的卡片。
核心要点回顾:
- onAddForm只执行一次,必须立即返回数据,不能做耗时操作
- onUpdateForm是同步回调,必须同步返回缓存数据
- onRemoveForm是最终回调,必须清理所有资源
- 定时刷新不是精确的,系统会根据条件决定是否触发
- FormAbility可能随时被销毁,状态必须外部存储
- 5分钟更新限制是硬性的,用更新队列管理
| 评估维度 | 说明 |
|---|---|
| 学习难度 | ⭐⭐⭐⭐ 回调时序、状态管理、错误恢复都需要深入理解 |
| 使用频率 | ⭐⭐⭐⭐⭐ 每个卡片都涉及生命周期管理 |
| 重要程度 | ⭐⭐⭐⭐⭐ 生命周期管理不好,卡片就是定时炸弹 |
下一篇聊卡片交互——点击跳转、消息事件、深链接,让卡片从"能看"变成"能用"。
- 点赞
- 收藏
- 关注作者
评论(0)