卡片代碼封裝:桌面卡片連續(xù)點(diǎn)擊導(dǎo)致重復(fù)提交如何做冪等)
HarmonyOS 7.0 API26 互動(dòng)卡片代碼封裝桌面卡片連續(xù)點(diǎn)擊導(dǎo)致重復(fù)提交如何做冪等這篇只拆一個(gè)具體點(diǎn)互動(dòng)卡片。版本邊界先放前面下面的寫法面向 HarmonyOS 7.0 / API 26。工程里如果還在混用舊 SDK、舊模擬器鏡像或舊設(shè)備系統(tǒng)先不要直接照搬代碼先把版本對(duì)齊。這個(gè)問題為什么值得單獨(dú)拆開發(fā)者真正會(huì)踩坑的地方不是接口名記不住而是版本、設(shè)備形態(tài)、資源狀態(tài)和頁(yè)面生命周期湊到一起以后問題才出現(xiàn)。以 互動(dòng)卡片 為例桌面卡片連續(xù)點(diǎn)擊導(dǎo)致重復(fù)提交如何做冪等。如果只看單次點(diǎn)擊很難發(fā)現(xiàn)必須把觸發(fā)條件、失敗日志和兜底路徑一起設(shè)計(jì)。這類問題的麻煩點(diǎn)是代碼經(jīng)常能編譯頁(yè)面第一次打開也像是正常的但一到折疊屏、多窗口、后臺(tái)恢復(fù)、跨設(shè)備入口或?qū)徍藱C(jī)型上行為就開始不穩(wěn)定。我的處理方式不是先改 UI而是先把能力邊界、觸發(fā)條件、失敗原因和兜底方案拆開。先對(duì)齊官方能力邊界參考點(diǎn)要確認(rèn)什么落到代碼里怎么處理HarmonyOS 7.0 / API 26 官方能力說(shuō)明確認(rèn)版本邊界和設(shè)備支持范圍先做能力判斷再進(jìn)入新能力分支ArkUI / ArkTS API 參考確認(rèn)組件生命周期、參數(shù)和異常返回把調(diào)用收口到 policy 或 guard不散落在頁(yè)面里應(yīng)用質(zhì)量與上架審核建議確認(rèn)權(quán)限、兼容和異常兜底把失敗原因?qū)戇M(jìn)日志和自測(cè)清單這里要避免一個(gè)常見誤區(qū)看到 7.0 新能力就直接在頁(yè)面里調(diào)用。更穩(wěn)的做法是先做一層能力判斷判斷通過再進(jìn)入新能力分支判斷失敗就明確走兜底日志里也要能看出失敗原因。兩個(gè)容易復(fù)現(xiàn)的案例案例一桌面卡片連續(xù)點(diǎn)擊導(dǎo)致重復(fù)提交如何做冪等復(fù)現(xiàn)方式不要做得太復(fù)雜。先把頁(yè)面打開到目標(biāo)狀態(tài)再連續(xù)觸發(fā)兩次能力入口。這個(gè)時(shí)候重點(diǎn)看三個(gè)點(diǎn)狀態(tài)有沒有丟、資源有沒有重復(fù)申請(qǐng)、失敗時(shí)有沒有明確原因。案例二卡片跨設(shè)備刷新延遲如何給出可見狀態(tài)第二個(gè)場(chǎng)景更接近線上用戶不會(huì)按開發(fā)者預(yù)設(shè)路徑操作他會(huì)切后臺(tái)、恢復(fù)、換方向、分屏、拖拽、鎖屏再回來(lái)。只看單次點(diǎn)擊問題很容易被遮住。拆法先決策再執(zhí)行再兜底我會(huì)把實(shí)現(xiàn)拆成三層1. 能力判斷層只判斷版本、設(shè)備形態(tài)、入口參數(shù)和依賴狀態(tài)。2. 執(zhí)行層只負(fù)責(zé)調(diào)用具體 API不混入頁(yè)面展示邏輯。3. 兜底層能力不可用時(shí)給舊方案、提示或延遲重試不讓頁(yè)面進(jìn)入半壞狀態(tài)。這樣拆的好處是后面升級(jí) SDK 或換設(shè)備時(shí)不需要在每個(gè)頁(yè)面里翻 if 判斷。頁(yè)面只拿一個(gè)明確結(jié)果能用、不能用、為什么不能用。Demo把能力判斷收口到一個(gè) guardtype CapabilityStatus enable | fallback | blocked; interface CapabilityInput { apiLevel: number; deviceType: string; scene: string; resourceReady: boolean; } interface CapabilityDecision { status: CapabilityStatus; reason: string; nextAction: string; } class Api26CapabilityPolicy { decide(input: CapabilityInput): CapabilityDecision { if (input.apiLevel 26) { return { status: fallback, reason: api-level-below-26, nextAction: use-old-path }; } if (!input.deviceType || input.deviceType unknown) { return { status: blocked, reason: device-type-unknown, nextAction: collect-device-info }; } if (!input.resourceReady) { return { status: fallback, reason: resource-not-ready, nextAction: show-lightweight-ui }; } return { status: enable, reason: api26-ready, nextAction: 互動(dòng)卡片-enabled }; } } Entry Component struct CapabilityDemoPage { State private result: string waiting; private policy: Api26CapabilityPolicy new Api26CapabilityPolicy(); private verify(): void { const decision this.policy.decide({ apiLevel: 26, deviceType: foldable, scene: main-entry, resourceReady: true }); this.result decision.status : decision.reason : decision.nextAction; console.info([api26-policy], this.result); } build() { Column({ space: 16 }) { Text(API 26 capability check).fontSize(20).fontWeight(FontWeight.Bold) Text(this.result).fontSize(16) Button(run verify).onClick(() this.verify()) }.width(100%).padding(20) } }這個(gè) Demo 只做一件事先判斷能力條件再把結(jié)果交給頁(yè)面。頁(yè)面不直接關(guān)心 API 細(xì)節(jié)也不把版本判斷散落在 build 里。后面要接真實(shí)頁(yè)面時(shí)可以把 HarmonyFeatureGuard 放到公共模塊里復(fù)用。異常日志應(yīng)該長(zhǎng)什么樣[feature-check] scenecapability, statusfailed, reasonapi-level-too-low [feature-check] scenecapability, statusfailed, reasondevice-mode-empty [feature-check] scenecapability, statuspassed, cost3ms日志不要只打印“失敗了”。至少要帶上 scene、status、reason 和耗時(shí)。否則出了問題以后只能靠猜。驗(yàn)證矩陣場(chǎng)景輸入條件預(yù)期結(jié)果關(guān)鍵日志桌面卡片連續(xù)點(diǎn)擊導(dǎo)致重復(fù)提交如何做冪等API 26 支持設(shè)備進(jìn)入新能力分支statuspassed卡片跨設(shè)備刷新延遲如何給出可見狀態(tài)API 26 狀態(tài)恢復(fù)不重復(fù)申請(qǐng)資源reusetrue舊版本兼容檢查API 25 或能力缺失走兜底分支statusfailed, fallbacktrue異常輸入檢查設(shè)備形態(tài)未知或入口參數(shù)缺失給出可定位原因reason 非空跑完后應(yīng)該看到的結(jié)果case: api26_foldable_enable - passed case: api25_fallback - passed case: empty_device_mode - passed case: resume_without_duplicate_request - passed我會(huì)怎么選方案方案適合場(chǎng)景風(fēng)險(xiǎn)繼續(xù)沿用舊寫法舊頁(yè)面、小范圍兼容遇到 7.0 新能力邊界時(shí)不好排查在頁(yè)面內(nèi)臨時(shí)處理快速驗(yàn)證問題代碼容易散后面不好復(fù)用抽成獨(dú)立工具或組件多頁(yè)面、多設(shè)備、多狀態(tài)復(fù)用前期要把輸入輸出設(shè)計(jì)清楚我的選擇是第三種。只要這個(gè)能力會(huì)被多個(gè)頁(yè)面用到就不要把判斷邏輯塞在頁(yè)面里。頁(yè)面只負(fù)責(zé)展示能力邊界、異常兜底、版本判斷放到獨(dú)立函數(shù)或組件里。這樣后面改 SDK、換設(shè)備、補(bǔ)兼容邏輯影響面會(huì)小很多。排查順序1. 先看 SDK 和設(shè)備 API 版本不一致就不要繼續(xù)猜頁(yè)面代碼。2. 再看入口參數(shù)和設(shè)備形態(tài)很多問題不是 UI 寫錯(cuò)而是能力條件根本不滿足。3. 再看日志里的失敗原因日志只打印 failed 沒有 reason后面一定會(huì)浪費(fèi)時(shí)間。4. 最后再把能力判斷抽出去頁(yè)面只消費(fèi)結(jié)果不直接散落版本判斷??梢栽趺磸?fù)用這個(gè)寫法可以繼續(xù)擴(kuò)成一個(gè)小工具輸入 featureName、apiLevel、deviceMode、entryState輸出 passed / failed / fallback 和 reason。頁(yè)面層只根據(jù)結(jié)果更新 UI。這樣做雖然前期多寫幾行代碼但后面接更多 HarmonyOS 7.0 能力時(shí)判斷邏輯不會(huì)越寫越散。最后總結(jié)這篇把 互動(dòng)卡片 放到 HarmonyOS 7.0 / API 26 的版本邊界里分析用兩個(gè)場(chǎng)景說(shuō)明問題怎么復(fù)現(xiàn)再用 guard、日志和驗(yàn)證矩陣把處理方式固定下來(lái)。這類特性真正有價(jià)值的地方不是知道一個(gè)新名字而是知道它在什么場(chǎng)景該用、什么時(shí)候不該用、怎么復(fù)現(xiàn)問題、怎么把修復(fù)沉淀成可復(fù)用代碼。后面再接復(fù)雜頁(yè)面時(shí)先把這個(gè)小 Demo 跑通基本能避開一半低級(jí)返工。