桌面学习卡片的价值不是把应用首页缩小后放到桌面,而是把“今天学什么”和“点一下去哪里”前置。用户不需要先打开应用、切换学习标签、找到每日一题,再判断是否完成;卡片应该直接给出当日任务,并把一次点击送到对应页面。
本文基于 化学实验室 1.0.5 的真实 HarmonyOS ArkTS 源码,拆解 DailyStudyFormAbility.ets、DailyStudyCard.ets、form_config.json、module.json5 和 EntryAbility.ets。当前工程已经声明 FormExtensionAbility,支持 2*2 与 2*4,配置了 08:00 刷新,并用 FormLink 携带 source: 'daily_study_card' 拉起 EntryAbility。表面链路完整,继续沿数据和路由逐步检查,却能发现三个关键断点。
第一,扩展能力通过 buildFormData() 创建了标题、任务和提示,但卡片页面把同样的文案全部写死,没有读取绑定数据,因此 onUpdateForm() 即使成功也看不出任何变化。第二,更新回调每次返回的仍是同一组静态字符串,08:00 或周期更新都不会产生“今日”差异。第三,FormLink 传入的 source 没有在 EntryAbility 的冷启动或热启动生命周期中被消费,应用最终仍加载 pages/Index 的首页标签。
本文的核心判断是:桌面卡片不是一张会跳转的海报,而是一条由数据契约、刷新时机、卡片快照和启动路由共同组成的服务直达链路。 只实现其中一个环节,用户就会看到“刷新了但没变化”或“点击了却还要自己找入口”。
本文唯一校验标记:CHEM-LAB-ONE12-DAILY-STUDY-FORM-CLOSURE-20260730

本文将完成这些工程问题的拆解:
- 还原当前卡片从配置发现到系统更新的真实链路。
- 让 FormBindingData 成为卡片页面的唯一数据来源。
- 用日期键、任务 ID、完成态和目标路由构造稳定契约。
- 设计冷启动与热启动都能消费的卡片跳转参数。
- 区分 2*2 与 2*4 的信息密度,而不是简单拉伸。
- 明确刷新策略、失败回退、实例清理和日志边界。
- 给出可复现的真机测试矩阵与故障定位顺序。
一、先理解卡片提供方与卡片使用方
在当前场景中,化学实验室是卡片提供方,系统桌面是卡片使用方。应用负责声明卡片、生成数据、实现 UI 和处理交互;桌面负责添加、展示、删除和触发系统调度。
官方 Form Kit 术语将静态 ArkTS 卡片描述为适合 UI 相对固定、交互简单的场景:卡片渲染完成后,宿主使用最后一帧快照并释放运行资源。对于“每天展示一个学习任务、点击进入应用”的需求,这种形态是合理的,没有必要为了一个入口升级为长期运行的复杂动态卡片。
这里的“静态”不等于“永远不更新”。提供方仍可以在添加和更新回调中生成新的绑定数据,再由系统刷新卡片快照。
二、当前工程的四个声明位置
卡片能被系统发现,至少依赖四处一致配置:
当前 module.json5 的关键配置为:
"extensionAbilities": [
{
"name": "DailyStudyFormAbility",
"srcEntry": "./ets/formability/DailyStudyFormAbility.ets",
"label": "$string:daily_study_form_label",
"description": "$string:daily_study_form_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
这一层已经正确建立了“系统配置 -> 扩展能力 -> 卡片描述文件”的引用关系。后续排障时,只有系统完全看不到卡片,才优先检查这些声明;如果卡片能添加但内容不更新,应转向数据绑定链路。
三、form_config.json 决定系统怎样看待卡片
当前配置支持 2*2 和 2*4,默认尺寸是 2*2:
{
"name": "DailyStudyCard",
"src": "./ets/widget/pages/DailyStudyCard.ets",
"uiSyntax": "arkts",
"colorMode": "auto",
"updateEnabled": true,
"scheduledUpdateTime": "08:00",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": ["2*2", "2*4"]
}
这里应重点验证三件事。第一,支持尺寸必须有真实布局适配。第二,colorMode: auto 只有在页面使用可随模式变化的资源时才有意义。第三,定点和周期更新字段应根据项目目标 SDK 的官方说明选用并在真机验证,不应只因为模板生成了两个字段就同时保留。
每日学习任务通常不需要高频刷新。若内容只在日期变化或用户完成任务后改变,可以采用“每日定点 + 业务事件主动更新”的组合,避免无意义周期唤醒。
四、当前扩展能力的生命周期实现
DailyStudyFormAbility 已覆盖创建、更新和删除:
export default class DailyStudyFormAbility extends FormExtensionAbility {
onAddForm(want: Want): formBindingData.FormBindingData {
return buildFormData()
}
onUpdateForm(formId: string): void {
formProvider.updateForm(formId, buildFormData()).catch((err: Error) => {
hilog.error(DOMAIN, 'DailyStudyForm',
'update form failed: %{public}s', err.message)
})
}
onRemoveForm(formId: string): void {
hilog.info(DOMAIN, 'DailyStudyForm', 'onRemoveForm: %{public}s', formId)
}
}
生命周期框架本身没有缺失。真正的问题是 buildFormData() 返回什么、卡片页面是否读取它,以及点击后应用是否消费路由参数。
五、数据提供方与卡片 UI 目前各写了一份文案
扩展能力构造了:
interface DailyStudyFormData {
title: string
subtitle: string
task: string
tip: string
}
但 DailyStudyCard.ets 又直接写了“今日化学学习”“完成一道化学题”等同样文本。两份内容暂时一样,容易让开发者误以为绑定成功;只要把 task 改成另一个值再更新,就会发现卡片没有变化。
这是一类典型的“假闭环”:提供方生成了数据,更新 API 也返回成功,但渲染页根本没有消费。判断刷新是否有效,必须以桌面实际快照变化为准,不能只看 updateForm() Promise 成功。
六、把绑定字段接入 ArkTS 卡片页面
卡片页面应为绑定字段提供状态入口,默认值只用于数据尚未就绪时的可控展示:
@Entry
@Component
struct DailyStudyCard {
@StorageProp('title') title: string = '今日化学学习'
@StorageProp('subtitle') subtitle: string = '准备今天的学习任务'
@StorageProp('taskTitle') taskTitle: string = '打开应用查看任务'
@StorageProp('statusText') statusText: string = '待开始'
@StorageProp('targetRoute') targetRoute: string = 'daily_quiz'
build() {
// Text(this.title)、Text(this.taskTitle) 等从绑定字段渲染
}
}
字段名称必须与 createFormBindingData() 中的对象键完全一致。默认值不承担每日业务逻辑,只保证异常情况下卡片不出现空白。
七、重新定义每日学习卡片的数据契约
仅有四段展示文本不足以支持去重、更新、跳转和完成态。建议把数据契约扩展为:
export interface DailyStudyFormData {
schemaVersion: number
dateKey: string
taskId: string
taskType: 'quiz' | 'reading' | 'experiment_review'
title: string
subtitle: string
taskTitle: string
statusText: string
completed: boolean
targetRoute: string
}
schemaVersion 用于未来兼容字段变化,dateKey 判断是否跨日,taskId 保证内容身份稳定,taskType 决定图标和目标页面,completed 决定视觉状态。卡片 UI 不再根据中文文案猜业务状态。
八、数据生成不能散落在 FormExtensionAbility 中
扩展能力应管理生命周期,业务数据由仓库或服务提供:
export interface DailyStudyTask {
id: string
dateKey: string
type: 'quiz' | 'reading' | 'experiment_review'
title: string
completed: boolean
targetRoute: string
}
export class DailyStudyRepository {
async getTask(dateKey: string): Promise<DailyStudyTask> {
// 从项目现有本地数据服务读取,失败时返回受控默认任务
return this.buildFallbackTask(dateKey)
}
}
如果卡片数据来自本地持久化,仓库应使用扩展能力可用的上下文初始化存储。不要让卡片页面直接访问应用页面的内存状态,因为卡片渲染与应用 UI 不是同一个页面生命周期。
九、创建卡片时要记录实例维度
一个用户可能同时添加多个卡片实例,且尺寸不同。onAddForm(want) 中的参数可以用于识别实例与维度;解析时应做类型保护,不要直接假设字段一定存在:
interface FormRequestInfo {
formId: string
dimension: number
}
function readFormRequest(want: Want): FormRequestInfo {
const params = want.parameters
const formId = String(params?.['ohos.extra.param.key.form_identity'] ?? '')
const rawDimension = Number(params?.['ohos.extra.param.key.form_dimension'] ?? 0)
return {
formId,
dimension: Number.isFinite(rawDimension) ? rawDimension : 0
}
}
具体系统参数键和可用 API 应以项目目标 SDK 的 Form Kit 文档为准。示例的重点是:系统输入先规范化,再进入业务逻辑;日志和持久化只保存确实需要的字段。
十、不要把完整 want.parameters 作为公开日志
当前代码把 JSON.stringify(want.parameters) 以 %{public}s 输出。现阶段参数主要是卡片元数据,但未来一旦加入用户选择、任务 ID 或来源信息,完整公开日志会扩大泄露范围。
更稳的日志只记录排障所需字段:
hilog.info(
DOMAIN,
'DailyStudyForm',
'add form, id=%{public}s, dimension=%{public}d',
request.formId,
request.dimension
)
任务标题、学习记录和用户内容不应写入公开日志。错误日志记录错误码与受控消息,不输出整个对象。
十一、构造数据时要让日期和任务真正变化
可把“当前日期 -> 今日任务 -> 卡片契约”集中在一个构造器中:
function toFormData(task: DailyStudyTask): DailyStudyFormData {
return {
schemaVersion: 1,
dateKey: task.dateKey,
taskId: task.id,
taskType: task.type,
title: '今日化学学习',
subtitle: task.completed ? '今日任务已完成' : '每天前进一小步',
taskTitle: task.title,
statusText: task.completed ? '已完成' : '待完成',
completed: task.completed,
targetRoute: task.targetRoute
}
}
同一天的同一个任务应生成稳定 taskId,避免每次更新都变成新任务;跨日后 dateKey 和任务身份才更新。
十二、更新流程要有加载失败回退
onUpdateForm() 返回 void,内部可以启动异步更新,但必须在 Promise 链中收口错误:
onUpdateForm(formId: string): void {
this.refreshForm(formId).catch((error: Error) => {
hilog.error(
DOMAIN,
'DailyStudyForm',
'refresh failed, id=%{public}s, reason=%{public}s',
formId,
error.message
)
})
}
private async refreshForm(formId: string): Promise<void> {
const dateKey = this.clock.todayKey()
const task = await this.repository.getTask(dateKey)
const binding = formBindingData.createFormBindingData(toFormData(task))
await formProvider.updateForm(formId, binding)
}
这里把时钟也抽成依赖,是为了在测试中固定日期,验证跨日更新而不等待第二天。

十三、每日卡片应该选择什么刷新策略
当前配置同时存在 scheduledUpdateTime: '08:00' 与 updateDuration: 1。不同 API 版本对更新字段的约束和调度行为需要按目标 SDK 官方文档确认,工程上不应依赖未验证的组合。
对于化学学习卡片,可以定义清楚的业务策略:
系统调度不是精确定时器,测试时应允许延迟;业务正确性不能依赖“08:00 整秒一定回调”。
十四、完成任务后如何主动刷新卡片
当用户在应用内完成每日一题,业务层应先保存完成记录,再通知卡片更新。不要让答题页直接拼装卡片字段,可以调用统一服务:
export class DailyStudyCardUpdater {
async refresh(formIds: string[]): Promise<void> {
const task = await this.repository.getTask(this.clock.todayKey())
const data = formBindingData.createFormBindingData(toFormData(task))
for (const formId of formIds) {
await formProvider.updateForm(formId, data)
}
}
}
如果项目不需要保存实例 ID,就采用 Form Kit 针对当前版本提供的查询或更新方案;若保存实例 ID,必须在删除回调中清理失效记录。
十五、onRemoveForm 是否需要清理取决于是否保存实例状态
当前 onRemoveForm() 只记录日志。在完全无状态的卡片中,这足够;一旦为不同卡片保存尺寸、任务偏好或实例 ID,就必须删除对应数据:
onRemoveForm(formId: string): void {
this.formInstanceRepository.remove(formId).catch((error: Error) => {
hilog.error(DOMAIN, 'DailyStudyForm',
'remove instance failed: %{public}s', error.message)
})
}
删除回调应幂等:记录已经不存在时也视为成功,避免系统重复通知造成异常。
十六、当前 FormLink 只证明能拉起应用
卡片页面把整个内容包在 FormLink 中:
FormLink({
action: 'router',
abilityName: 'EntryAbility',
params: {
source: 'daily_study_card'
}
}) {
// card content
}
它能够携带来源参数拉起 EntryAbility,但“拉起应用”和“直达学习任务”不是同一件事。当前 EntryAbility.onCreate() 没有读取 want.parameters,也没有 onNewWant();随后固定加载 pages/Index。卡片点击因此只完成了链路前半段。
十七、为跳转定义稳定参数,不能只传 source
建议传递最小、可验证的启动契约:
export interface DailyStudyLaunchParams {
source: 'daily_study_card'
target: 'daily_quiz' | 'reading' | 'experiment_review'
taskId: string
dateKey: string
}
source 用于统计和回退,target 决定页面,taskId 定位内容,dateKey 防止用户点击昨天的快照后误打开今天的另一条任务。卡片不传完整题目正文,避免参数过大和数据过期。
十八、冷启动与热启动必须分别处理
应用未运行时,参数从 onCreate(want, …) 进入;应用已在前台或后台时,新的启动请求可能进入 onNewWant(want, …)。两条生命周期都应调用同一个规范化函数:
private acceptLaunchWant(want: Want): void {
const launch = DailyStudyLaunchParser.parse(want.parameters)
if (launch === undefined) {
return
}
AppStorage.setOrCreate<DailyStudyLaunchParams>('pendingDailyLaunch', launch)
}
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.acceptLaunchWant(want)
// 其余初始化
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.acceptLaunchWant(want)
}
具体生命周期签名以项目当前 SDK 类型定义为准。核心是冷、热启动共享解析逻辑,避免一种场景能直达、另一种只回首页。
十九、页面加载与数据初始化存在竞态
当前 EntryAbility.onCreate() 异步执行 DataStore.init(),onWindowStageCreate() 直接加载 pages/Index。如果卡片目标页依赖存储,路由消费可能早于数据初始化完成。
可以让启动协调器保存两个条件:页面已就绪、数据已就绪。只有两者都满足时才消费 pendingDailyLaunch:
interface StartupState {
contentReady: boolean
dataReady: boolean
}
private tryDispatchPendingLaunch(): void {
if (!this.startupState.contentReady || !this.startupState.dataReady) return
this.launchCoordinator.dispatchPending()
}
如果任务读取失败,回退到学习首页并显示可恢复提示,不能留下白屏或无响应点击。
二十、Index 页需要接住学习标签与二级页面目标
当前 Index.ets 的 currentIndex 默认为 0,学习标签是索引 2。最小改造是先让启动协调器切换到学习标签,再由页面路由进入 DailyQuizPage:
@StorageLink('requestedTabIndex') requestedTabIndex: number = 0
aboutToAppear(): void {
this.currentIndex = this.requestedTabIndex
this.tabController.changeIndex(this.requestedTabIndex)
}
二级页面跳转应继续走项目已有路由方式。若路由失败或目标任务已过期,停留在学习标签,而不是返回空白页。
二十一、旧卡片快照必须有过期处理
用户可能在第二天 08:00 刷新前点击昨天卡片。应用收到 dateKey 后应与当前日期比较:
if (launch.dateKey !== this.clock.todayKey()) {
return {
target: 'learning_home',
message: '卡片内容已更新,请查看今天的学习任务'
}
}
这比盲目使用 taskId 更安全。页面可以随后触发一次卡片刷新,让桌面快照尽快与当天内容一致。
二十二、2×2 与 2×4 不应该共用同一信息密度
当前配置声明两个尺寸,但只有一个卡片页面布局。它可以被拉伸,却没有利用 2*4 的额外空间。建议保持同一数据契约,按尺寸设计两种信息层级:
| 2×2 | 标题、今日任务、完成态、打开按钮 | 一行简短副标题 |
| 2×4 | 2×2 全部内容 | 学习连续天数、预计用时、下一任务 |
尺寸适配不是把字号放大。2×2 要保证最长任务名省略后仍能看懂,2×4 则利用宽度提供一个额外决策信息点。
二十三、卡片 UI 要从硬编码颜色迁移到资源
DailyStudyCard.ets 直接使用 #0052D9、#047481、#FFFFFF 等颜色,同时配置了 colorMode: auto。如果没有深浅色资源映射,“auto”不会自动让硬编码渐变和文字达到合理对比。
卡片资源至少应区分:背景起止色、主文字、次文字、状态标签、按钮背景和完成态。文字在渐变最亮和最暗位置都要可读;系统深色模式、不同壁纸和桌面缩放下都需要截图验证。
二十四、点击区域要明确且不制造误触
当前整个卡片都是 FormLink,适合只有一个动作的静态入口。但卡片内部又绘制了“学习”和“打开”两个视觉按钮,用户可能误以为它们有不同功能。
如果卡片只有一个目标,保留整卡点击,同时把视觉按钮统一成一个明确动作;如果 2×4 未来出现多个入口,应分别定义交互区域,并确保参数目标不同。交互设计不能比实际能力更复杂。
二十五、卡片数据的空态、错误态和完成态
卡片没有普通页面那么多空间,但仍需要三类受控状态:
export type CardContentState = 'ready' | 'completed' | 'fallback'
- ready:显示任务与“去完成”。
- completed:显示已完成和次日提示,不重复催促。
- fallback:数据读取失败时显示“打开应用继续学习”,仍保留安全入口。
不要在卡片上展示技术错误码。错误码进入日志,用户看到的是可执行的回退动作。
二十六、职责拆分后的整体结构
推荐的依赖方向是:
FormExtensionAbility
-> DailyStudyCardService
-> DailyStudyRepository
-> FormDataMapper
-> FormProviderAdapter
DailyStudyCard UI
-> FormBindingData fields
-> FormLink launch contract
EntryAbility
-> LaunchParser
-> StartupCoordinator
-> Index / target page
扩展能力不拼业务文案,卡片 UI 不查持久化,入口 Ability 不理解题目数据。每一层只处理一种变化原因。

二十七、对构造器做纯函数测试
toFormData() 不依赖系统上下文,最适合先写单元测试:
const task: DailyStudyTask = {
id: 'quiz-20260717',
dateKey: '2026-07-17',
type: 'quiz',
title: '完成一道酸碱平衡题',
completed: false,
targetRoute: 'daily_quiz'
}
const data = toFormData(task)
expect(data.taskId).assertEqual('quiz-20260717')
expect(data.statusText).assertEqual('待完成')
expect(data.targetRoute).assertEqual('daily_quiz')
还要验证完成态、空标题回退、超长标题截断策略和未知任务类型。纯函数测试失败时,不必启动桌面卡片环境。
二十八、用适配器测试更新失败
直接调用系统 formProvider 不易制造异常,可以封装窄接口:
export interface FormUpdater {
update(formId: string, data: DailyStudyFormData): Promise<void>
}
export class SystemFormUpdater implements FormUpdater {
async update(formId: string, data: DailyStudyFormData): Promise<void> {
const binding = formBindingData.createFormBindingData(data)
await formProvider.updateForm(formId, binding)
}
}
测试替身可以模拟实例不存在、数据加载失败和连续两次更新。服务层应记录失败但不崩溃,也不能把一个实例失败误判成所有实例失败。
二十九、冷启动与热启动测试矩阵
| 冷启动点击 | 应用未运行 | 初始化完成后进入每日一题 |
| 热启动点击 | 应用停在实验室标签 | 切换学习标签并进入目标页 |
| 旧日期点击 | 卡片 dateKey 为昨天 | 回退学习首页并提示内容更新 |
| 未知 target | 参数被篡改或版本过旧 | 忽略目标,安全进入学习首页 |
| 数据初始化失败 | 本地仓库不可用 | 不白屏,展示可重试提示 |
| 重复点击 | 连续点击同一卡片 | 不叠加重复目标页 |
如果只验证冷启动,应用已在运行时的 onNewWant 漏洞很容易带到发布版本。
三十、卡片显示测试矩阵
| 普通任务 | 标题与动作完整 | 增加辅助信息 |
| 超长任务 | 单行省略且主动作可见 | 最多两行,不挤压按钮 |
| 完成态 | 明确“已完成” | 展示下一步建议 |
| 数据失败 | 回退文案和入口 | 回退文案和入口 |
| 深色模式 | 文字、渐变、状态标签可读 | 同左 |
| 大字体 | 不重叠、不截断核心动作 | 信息按优先级收缩 |
还要在手机、平板和 2in1 的桌面环境中验证实际卡片支持情况。应用声明支持这些设备,不代表桌面卡片布局自动适配完成。
三十一、常见故障一:系统完全找不到卡片
按下面顺序检查:
此时不要先改业务数据,因为系统尚未走到数据阶段。
三十二、常见故障二:updateForm 成功但界面不变
先把绑定字段临时改成可辨识值,例如标题追加日期。若桌面仍不变,检查:
当前项目首先应修复第 1 项,因为卡片页面仍使用硬编码文本。
三十三、常见故障三:点击卡片只回首页
确认 FormLink 的 abilityName 能匹配 EntryAbility 后,继续检查:
当前 DailyQuizPage 已在 main_pages.json 中,主要缺口是参数接收和启动协调。
三十四、常见故障四:08:00 没有准时刷新
系统卡片更新受版本、调度策略、设备状态和配置约束影响,不应当作精确闹钟。排查时记录实际回调时间,确认 updateEnabled 与目标 SDK 配置有效,并在业务完成事件后主动更新。
若每日内容必须在首次查看时正确,卡片点击进入应用后还应重新校验当前日期,不能把正确性完全押在系统定点刷新上。
三十五、真机验收清单
三十六、分阶段落地更容易定位问题
第一阶段只把 FormBindingData 接入卡片页面,用一个可辨识日期字段证明数据闭环。第二阶段接入每日任务仓库和完成态。第三阶段定义启动契约,分别打通冷、热启动。第四阶段完成 2×2、2×4 和主题适配。第五阶段补主动刷新、实例清理和真机回归。
每阶段只改变一个验证点:看到卡片变化、看到任务变化、看到页面直达、看到尺寸适配、看到完成态同步。这样一旦失败,可以立刻定位到对应层。
三十七、完成标准应该写成可观察结果
这张卡片的验收不是“代码里有 FormExtensionAbility”,而是:
- 系统能发现并添加卡片。
- 卡片显示的是绑定数据,不是页面硬编码副本。
- 日期或完成态变化后,桌面快照能被更新。
- 点击参数在冷启动和热启动都被消费。
- 目标无效或数据失败时,安全回退到学习首页。
- 2×2 与 2×4 都提供清晰、不过载的信息。
三十八、官方资料与版本核对入口
本文结合项目真实源码与当前工程结构给出改造方案。Form Kit 配置字段、生命周期签名和交互能力会随 API 版本演进,落地时应以项目目标 SDK 的华为官方文档为准:
- Form Kit 术语与卡片角色
- Form Kit 产品与能力入口
- HarmonyOS 开发文档中心
特别是更新策略、系统参数键和 onNewWant 的准确签名,应在 DevEco Studio 当前 SDK 类型提示中再次确认,并通过真机回调日志验证。
三十九、源码事实与建议实现的边界
为了避免把设计方案写成已经上线的能力,下面将当前仓库可以复核的 Form Kit 实现与建议演进明确分开。
| 扩展能力 | DailyStudyFormAbility 继承 FormExtensionAbility,实现添加、更新和移除回调 | 保留系统生命周期入口,把数据构建委托给可测试的每日任务服务 |
| 初始数据 | buildFormData() 返回标题、副标题、任务和提示四个固定字符串 | 使用日期键、任务 ID、完成态、目标路由和版本字段构建稳定契约 |
| 更新行为 | onUpdateForm 调用 formProvider.updateForm,但每次仍生成同一组静态字符串 | 更新时读取真实任务状态;失败时保留最近有效快照并记录可定位日志 |
| 卡片 UI | DailyStudyCard.ets 中标题、副标题和任务文案全部硬编码 | 从 FormBindingData 读取字段,避免提供方数据与 UI 副本分叉 |
| 点击行为 | FormLink 使用 router 动作,目标为 EntryAbility,参数只有 source: daily_study_card | 增加受控目标路由与任务 ID,并对未知目标做白名单回退 |
| 冷启动 | EntryAbility.onCreate 接收 Want,但当前没有消费卡片 source | 在初始化完成后解析启动契约,并把一次性导航意图交给页面路由层 |
| 热启动 | 当前 EntryAbility 没有实现卡片参数的 onNewWant 消费路径 | 按目标 SDK 的准确签名补热启动处理,避免应用已运行时点击失效 |
| 首屏加载 | onWindowStageCreate 固定加载 pages/Index | 继续加载稳定首屏,再由明确的导航状态直达目标或安全回退 |
| 尺寸与更新 | 配置支持 2*2、2*4,默认 2*2,启用更新并配置 08:00 与周期参数 | 为两个尺寸定义不同信息密度,并在目标 SDK 与真机上验证实际调度语义 |
| 主题策略 | 卡片配置使用 colorMode: auto,页面本身采用固定渐变与白色文字 | 验证深浅色、壁纸、大字体与快照对比度,必要时提供资源化主题 |
本文提出的每日任务仓库、版本化 DailyStudyFormData、日期键、完成态同步、尺寸分级布局、冷/热启动统一消费器和主动刷新策略,都是建议实现,当前源码尚未集成。本次精修没有执行 DevEco Studio 构建、卡片安装添加、08:00 调度、设备重启或真机点击直达测试,因此不能据此声称卡片数据闭环和服务直达已经修复;相关配置字段与生命周期签名还必须以项目目标 SDK 类型和真机日志为准。
总结
化学实验室现有桌面卡片已经完成了声明、扩展能力、卡片页面和 FormLink 的基础搭建,最值得修复的不是视觉细节,而是两条没有闭环的链路:FormBindingData 没有进入 UI,FormLink 参数没有进入业务页面。更新回调反复提交静态文案、两个尺寸共用同一信息密度,也让“每日学习入口”的产品价值没有真正体现。
把每日任务建模为带日期键、任务 ID、完成态和目标路由的稳定契约,再让 Form 扩展、卡片页面和启动路由共同消费这份契约,才能得到可信的服务直达体验。卡片显示什么、何时更新、点击去哪、失败如何回退,都必须能被真机观察和测试,而不是只在配置文件里看起来完整。
AI 辅助声明:本文由 AI 辅助完成源码梳理、结构校对与配图设计,技术结论均以文中列出的本地 ArkTS 源码为依据,并由作者复核后发布。




