
HarmonyOS 應用實戰 98隱私彈窗別擋住恢復路徑先保證可退出和可重開隱私彈窗最容易被寫成“首次啟動彈一次”。這樣做上線前看起來沒問題但遇到備份恢復、版本升級、用戶拒絕后再次進入就會暴露兩個風險沒有明確同意記錄或者彈窗擋住了退出和恢復路徑。“答案之書”當前工程里應用級偏好有firstLaunchDone但沒有獨立的隱私同意狀態。第 98 篇要說清楚這個邊界首次啟動完成不等于用戶同意隱私協議隱私狀態應該有版本、同意時間、拒絕出口和再次打開入口。當前偏好鍵只覆蓋啟動狀態libraryHAR/src/main/ets/models/AppPreferences.ets當前定義如下exportinterfaceAppPreferences{schemaVersion:number;currentDeckId:string;firstLaunchDone:boolean;}exportclassAppPrefKey{staticreadonlySchemaVersion:stringschemaVersion;staticreadonlyCurrentDeckId:stringcurrentDeckId;staticreadonlyFirstLaunchDone:stringfirstLaunchDone;staticreadonlyLastSeededVersion:stringlastSeededVersion;}這里的firstLaunchDone只能說明“首次啟動流程是否完成”。它不能說明用戶看過哪一版隱私文本不能說明用戶何時同意也不能支持撤回或重新查看。啟動鏈路也沒有隱私攔截點EntryAbility當前啟動時會初始化 Preferences 并運行SeedLoader然后加載首頁try{awaitPreferencesStore.init(ctx);awaitSeedLoader.run(ctx);}catch(err){hilog.error(DOMAIN,testTag,bootstrap failed: %{public}s,(errasError).message);}windowStage.loadContent(pages/Index,(err){if(err.code){hilog.error(DOMAIN,testTag,Failed to load the content. Cause: %{public}s,JSON.stringify(err));return;}hilog.info(DOMAIN,testTag,Succeeded in loading the content.);});這段代碼說明當前應用更關注“數據能否就緒”。如果要加隱私彈窗不能簡單塞在首頁最上層遮住所有內容而要設計清楚未同意時能退出已同意后能進入業務恢復或升級后能重新判斷協議版本。為什么不能用 firstLaunchDone 代替同意記錄兩者的含義不同字段代表什么不能代表什么firstLaunchDone首次引導或啟動初始化完成用戶同意隱私協議schemaVersion本地數據結構版本隱私文本版本LastSeededVersion默認題庫播種版本用戶授權狀態currentDeckId當前選擇題庫是否允許進入業務頁如果把隱私同意混在firstLaunchDone里用戶恢復備份后可能直接進入業務頁協議更新后也無法判斷是否需要重新展示。這不是 UI 問題而是狀態語義不清。建議新增獨立 ConsentRecord下面是建議補強模型不表示當前工程已經存在exportinterfacePrivacyConsentRecord{accepted:boolean;policyVersion:string;acceptedAt:number;source:first_open|settings|restore;}exportclassPrivacyPrefKey{staticreadonlyConsentRecord:stringprivacyConsentRecord;}policyVersion要和隱私文本版本綁定。只存一個 boolean 不夠因為協議內容更新后程序需要知道舊同意是否還能繼續使用。source也不是裝飾字段它能幫助排查“用戶是在首次打開同意還是恢復后重新確認”。用服務封住讀寫規則隱私狀態不建議散落在頁面里直接讀寫 Preferences。可以用一個服務集中處理classPrivacyConsentServiceImpl{asyncload():PromisePrivacyConsentRecord|null{returnawaitPreferencesStore.getJsonPrivacyConsentRecord|null(PrefStoreName.App,PrivacyPrefKey.ConsentRecord,null);}asyncisAccepted(policyVersion:string):Promiseboolean{constrecord:PrivacyConsentRecord|nullawaitthis.load();return!!recordrecord.acceptedrecord.policyVersionpolicyVersion;}asyncaccept(policyVersion:string,source:PrivacyConsentRecord[source]):Promisevoid{constrecord:PrivacyConsentRecord{accepted:true,policyVersion,acceptedAt:Date.now(),source};awaitPreferencesStore.setJson(PrefStoreName.App,PrivacyPrefKey.ConsentRecord,record);}}頁面不應該自己拼 key也不應該自己判斷版本兼容。頁面只負責展示協議、同意、拒絕、重新打開服務負責狀態含義和寫入位置。彈窗要有拒絕出口隱私彈窗不應只提供“同意”。用戶拒絕時至少要能退出當前業務入口不能被遮罩卡死BuilderfunctionPrivacyGate(){Column({space:16}){Text(隱私說明).fontSize(20).fontWeight(FontWeight.Medium)Text(請閱讀并確認本地題庫、收藏和提問歷史的使用方式。).fontSize(14)Row({space:12}){Button(不同意).onClick(()this.exitApp())Button(同意并進入).onClick(()this.acceptAndEnter())}}}exitApp()在真實工程里可以調用 Ability 上下文結束當前頁面或退回安全入口。關鍵是保留明確出口而不是讓用戶只能點同意才能繼續操作。恢復后要重新判斷而不是沿用舊界面狀態備份恢復會改變 Preferences隱私狀態也可能被帶回來。恢復鏈路完成后應該重新讀取同意記錄asyncfunctionafterRestore(policyVersion:string):Promisevoid{constok:booleanawaitPrivacyConsentService.isAccepted(policyVersion);if(!ok){AppStorage.setOrCreate(privacyGateVisible,true);return;}AppStorage.setOrCreate(privacyGateVisible,false);}這段邏輯的重點是“恢復后重新判斷”。不要因為當前頁面已經在業務態就默認恢復后的偏好仍然可信。尤其是跨版本恢復時協議版本必須重新對齊。和當前啟動流程怎么配合更穩的接入順序是EntryAbility初始化PreferencesStore。SeedLoader保證默認題庫和currentDeckId可用。首頁或統一入口讀取PrivacyConsentService.isAccepted()。未同意時展示可退出的隱私入口。同意后寫入policyVersion acceptedAt再開放業務操作。這樣做不會阻斷啟動自愈也不會把同意狀態和默認題庫播種混在一起。數據可以先就緒業務入口再根據隱私狀態決定是否可用。驗證路徑靜態確認rg-nFirstLaunchDone|PrivacyConsent|ConsentRecord|policyVersion|acceptedAtD:\ProgramData\huawei\lesson\The_Book_of_Answers\libraryHAR\src\main\etsD:\ProgramData\huawei\lesson\The_Book_of_Answers\entry\src\main\ets交互回歸場景期望結果首次安裝打開展示隱私入口拒絕可退出同意后重啟不重復彈同一版本協議協議版本升級重新展示并記錄新版本恢復舊備份重新判斷policyVersion用戶從設置重看能打開協議不改變同意時間除非重新同意本文沒有實際改 HarmonyOS 工程代碼也沒有執行真機隱私流程這些是落地方案時必須補跑的驗證項。常見問題隱私流程出問題時通常不是彈窗樣式不夠明顯而是狀態語義沒有分清。排查時先確認當前讀取的是啟動標記、協議版本還是用戶同意記錄再看拒絕和恢復路徑是否能走通。現象常見原因修復方向用戶拒絕后退不出去彈窗沒有拒絕分支提供退出或返回安全入口協議更新后不再彈只存 boolean加policyVersion恢復后直接進業務恢復鏈路沒有重讀同意狀態恢復完成后重新判斷同意記錄說不清來源只存 true/false保存acceptedAt和source收口隱私彈窗不是首次啟動裝飾。當前工程里的firstLaunchDone只能表示啟動流程不能替代隱私同意。要讓隱私流程可維護就把同意狀態獨立成PrivacyConsentRecord保留版本、時間、來源、拒絕出口和恢復后的重新判斷。這條邊界一旦立住后續擴展會簡單很多協議文本更新時只比較policyVersion備份恢復后只重新讀取同意記錄用戶拒絕時只退回安全入口。業務頁不用理解隱私狀態的存儲細節也不會因為一個啟動標記被誤用而繞過用戶選擇。