v73:桌面卡片刷新修复 + 首页日程/任务就地弹详情卡 + 一键导航
用户 2026-10-21 三条:
「卡片里面的数据并不刷新……应用里面的日程已经有了,但卡片上的日程却并不刷新」
「首页上的那个日程和任务,点击之后都应该像日程和任务里面的卡片一样弹出来,
不要进入到日程里面……有地址的还可以直接一键导航」
「任务也是一样,点击任务之后弹出来详情,可以标记它是不是完成了」
① 卡片不刷新(根因)
卡片进程(FormExtensionAbility)与应用进程靠 preferences 传 formIds / 翻页偏移。
SDK 明文禁止跨进程用 preferences,且 getPreferences() 在**进程内**缓存实例、
同名再取不读盘 ⇒ 应用进程可能永久持有空注册表 ⇒ pushAll() 首行
`if (list.length === 0) return` 直接返回 ⇒ 卡片只靠系统 30 分钟自刷。
修:store() 每次先 removePreferencesFromCacheSync 再取;并写死单写者纪律
(register/unregister/setOffset/clearOffset 只由卡片进程调,应用进程只读)。
② 首页点日程 → 弹 FbEventSheet(canEdit: false,首页不给编辑入口)
有地址的行整行可点 + 右侧「➤ 导航」胶囊;「编辑日程」才跳日历模块
(编辑是日历页的职责,不在首页复制第二份表单)。
③ 首页点任务 → 新的 FbTaskDetailSheet
状态圆可点(勾完成)、标题带删除线、底部「标记为已完成/未完成」+「改一改」。
状态**必须走 POST /tasks/<id>/status**(PATCH 会忽略 status)。
④ 一键导航 FbNav.ets:startAbility 隐式 Want(不是 openLink,那是 App Linking);
canOpenLink 需 module.json5 声明 querySchemes(未声明恒 false 且不抛错);
高德/百度/花瓣/腾讯按序探测,一个地图都没装则复制地点到剪贴板 + Toast。
⑤ 首页登记 FB_BACK(与其余 10 个模块一致),根容器 Stack(浮层是兄弟节点,
不能当 Column 子项排,否则卡片被挤出屏幕)。
静态守卫 905/0、v73 反向验证 17/0、assembleHap 通过、已装机启动。
This commit is contained in:
@@ -16,6 +16,36 @@
|
||||
* ⚠️ 偏移量做**夹取**(±366 天):`onFormEvent` 的 message 是外部输入,
|
||||
* 连续点 ‹ 几百次也不该把它算成一个荒唐的年份(`new Date(y, m, d + 99999)`
|
||||
* 在部分设备上会算出 Invalid Date,卡片直接白掉)。
|
||||
*
|
||||
* 🔴🔴 v73(用户 2026-10-21「卡片上的日程并不刷新」)—— **本文件的最大坑,别再踩**:
|
||||
*
|
||||
* 【症状】应用里日程已经改了/已经有了,桌面卡片上的日程**永远是旧的**;
|
||||
* 偶尔连"刚添加的卡片"都不带数据。
|
||||
*
|
||||
* 【根因】这份状态是**跨进程**的(写的一方是 `FbFormAbility` 那个卡片进程,
|
||||
* 读的一方是应用进程的 `FB_CARD_FORMS.pushAll`),而
|
||||
* `@ohos.data.preferences` 的 SDK 文档**明确写着**:
|
||||
* 「Preferences are **not thread-safe** and may cause file damage and
|
||||
* data loss when used in **multi-process** scenarios.
|
||||
* **Do not use preferences in multi-process scenarios.**」
|
||||
* 比"数据可能损坏"更直接的问题是**实例缓存**:
|
||||
* `getPreferences()` 把实例缓存在**进程内**,之后同名的调用
|
||||
* **直接返回缓存、不再读盘**。
|
||||
* ⇒ 应用进程只要在"桌面上还没有卡片"的时候读过一次 `fb_card_forms`,
|
||||
* 它就拿住了一个**空注册表**的缓存实例,此后**永远**读到空串
|
||||
* ⇒ `pushAll()` 第一行 `if (list.length === 0) return;` 直接返回
|
||||
* ⇒ 卡片**一次都收不到应用推的数据**(只剩系统 30 分钟自己刷)。
|
||||
* 偏移量也有同一个毛病:用户把卡翻到"明天",应用进程读到的还是缓存里的 0,
|
||||
* 推过去的数据是今天的 ⇒ 卡片"翻着翻着自己跳回今天"。
|
||||
*
|
||||
* 【修法】① 读之前**显式作废本进程的实例缓存**
|
||||
* (`removePreferencesFromCacheSync` + 重新 `getPreferences`),
|
||||
* 保证读到的永远是磁盘上的最新值;
|
||||
* ② 保持"**单写者**":本文件的 `register/unregister/setOffset/clearOffset`
|
||||
* **只被 `FbFormAbility`(卡片进程)调用**,应用进程**只读不写**
|
||||
* ⇒ 不会出现两个进程同时写同一个文件(那才是文档警告的"损坏"场景)。
|
||||
* ⚠️ 这条是硬约束:**以后不要让应用侧去写 formIds / 偏移量**。
|
||||
* 要写也得走"给卡片进程发消息、让它来写"。
|
||||
*/
|
||||
|
||||
import { common } from '@kit.AbilityKit';
|
||||
@@ -40,7 +70,34 @@ const SEP_ITEM: string = ';';
|
||||
const SEP_KV: string = '=';
|
||||
|
||||
class FbCardFormsImpl {
|
||||
/**
|
||||
* 取 preferences 实例 —— **每次都作废进程内缓存再取**。
|
||||
*
|
||||
* 🔴🔴 v73 的核心修复(详见文件头"最大坑"那段):
|
||||
* `getPreferences()` 会把实例缓存在进程内,之后同名调用**不再读盘**
|
||||
* ⇒ 卡片进程写进去的 formIds / 偏移量,应用进程**永远看不到**
|
||||
* ⇒ 日程改了、桌面卡片不刷新。
|
||||
* ⇒ 这里每次先用 `removePreferencesFromCacheSync` 把本进程的缓存摘掉,
|
||||
* 再 `getPreferences` 让系统**重新从磁盘读**。
|
||||
*
|
||||
* ⚠️ `removePreferencesFromCacheSync` 是 **API 10+**(本工程 compatibleSdk 24,够用);
|
||||
* 它**不会删数据**,只是把内存里的那份实例从缓存表里摘出来(名字里有
|
||||
* "remove" 很容易被误读成"删文件",其实是 `fromCache`)。
|
||||
* ⚠️ 摘缓存后,**旧实例不能再用**(SDK 原文:"Avoid using a removed Preferences
|
||||
* instance to perform data operations, which may cause data inconsistency")
|
||||
* ⇒ 所以本类**不缓存 `Preferences` 实例**,需要时现取。
|
||||
* ⚠️ 每次读写都重开有开销(~几毫秒),但本类的调用频率极低
|
||||
* (加卡/删卡/翻页/应用内改完日程推一次),完全不值得为此引入缓存。
|
||||
* 相比之下,"读到旧数据"的代价是**整个卡片功能失效**。
|
||||
*/
|
||||
private async store(ctx: common.Context): Promise<preferences.Preferences> {
|
||||
try {
|
||||
preferences.removePreferencesFromCacheSync(ctx, STORE_NAME);
|
||||
} catch (err) {
|
||||
// 摘缓存失败不致命:下面照样能取到(可能拿到缓存的旧实例),先记一条
|
||||
const e: BusinessError = err as BusinessError;
|
||||
hilog.warn(DOMAIN, TAG, '作废卡片状态缓存失败(继续用缓存实例):%{public}s', e.message);
|
||||
}
|
||||
return preferences.getPreferences(ctx, STORE_NAME);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user