feat: demo 同步 is_first_install 维度并修复 preferences 落盘

- StartupDiagnostics 新增 is_first_install 维度展示:preferences 无启动记录视为安装后首启,随后写入标记并 flushSync 同步落盘(与生产 StartManager.getAppInstallStatus 语义等价,演示工程无 biz_common 依赖故自实现)
- 修复 putSync 仅写内存缓存不落盘的问题:force-stop 后数据丢失,二次启动误判为首启;对比 AuthSession.flushSync 用法定位根因
- EntryAbility 调用 initialize 时传入 UIAbilityContext
- 真机验证:安装/清数据后首启 is_first_install=true,二次启动=false;全场景 3 组(拦截/登录/冷启动/热启动/强杀/补报)24 断言全部通过,耗时 127~165ms

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
clz
2026-08-06 11:16:28 +08:00
parent 0312555501
commit 6fe0d2ec7a
2 changed files with 18 additions and 2 deletions
@@ -7,6 +7,7 @@ import { common } from '@kit.AbilityKit';
import { inspector } from '@kit.ArkUI'; import { inspector } from '@kit.ArkUI';
import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit'; import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';
import { deviceInfo } from '@kit.BasicServicesKit'; import { deviceInfo } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
enum DrawReportState { enum DrawReportState {
IDLE, IDLE,
@@ -41,6 +42,11 @@ export class StartupDiagnostics {
* watcher 注册时间戳(epoch ms),用于识别系统补投递的上次启动遗留事件。 * watcher 注册时间戳(epoch ms),用于识别系统补投递的上次启动遗留事件。
*/ */
private static watcherRegisterTime: number = 0; private static watcherRegisterTime: number = 0;
/**
* 是否安装后首次启动(维度):preferences 无启动记录即为首次,随后写入标记。
* 与生产实现(StartManager.getAppInstallStatus)语义等价,演示工程无 biz_common 依赖故自实现。
*/
private static isFirstInstall: boolean = false;
private static watcherInitialized: boolean = false; private static watcherInitialized: boolean = false;
private static drawReportState: DrawReportState = DrawReportState.IDLE; private static drawReportState: DrawReportState = DrawReportState.IDLE;
private static capturedEntryPage: string = '未捕获'; private static capturedEntryPage: string = '未捕获';
@@ -57,10 +63,19 @@ export class StartupDiagnostics {
private static lastAction: string = '等待登录页首次绘制'; private static lastAction: string = '等待登录页首次绘制';
private static lastLaunchDetail: string = '尚未收到 APP_LAUNCH'; private static lastLaunchDetail: string = '尚未收到 APP_LAUNCH';
static initialize(): void { static initialize(context: common.UIAbilityContext): void {
if (StartupDiagnostics.watcherInitialized) { if (StartupDiagnostics.watcherInitialized) {
return; return;
} }
// 首次安装标志:preferences 无启动记录视为首次,随后写入并同步落盘;异常时按非首次处理
try {
const pref: preferences.Preferences = preferences.getPreferencesSync(context, { name: 'startup_demo' });
StartupDiagnostics.isFirstInstall = !pref.getSync('has_launched', false);
pref.putSync('has_launched', true);
pref.flushSync();
} catch (e) {
StartupDiagnostics.isFirstInstall = false;
}
const filter: hiAppEvent.AppEventFilter = { const filter: hiAppEvent.AppEventFilter = {
domain: hiAppEvent.domain.OS, domain: hiAppEvent.domain.OS,
@@ -273,6 +288,7 @@ export class StartupDiagnostics {
`appattach_to_appforeground_dur=${appattachToAppForegroundDur}ms\n` + `appattach_to_appforeground_dur=${appattachToAppForegroundDur}ms\n` +
`device_type=${StartupDiagnostics.getDeviceType()}, ` + `device_type=${StartupDiagnostics.getDeviceType()}, ` +
`system_version=${StartupDiagnostics.getSystemVersion()}\n` + `system_version=${StartupDiagnostics.getSystemVersion()}\n` +
`is_first_install=${StartupDiagnostics.isFirstInstall}\n` +
`补报事件=${isBackfill ? '是(entry_page 不归因,按 unknown 处理)' : '否'}`; `补报事件=${isBackfill ? '是(entry_page 不归因,按 unknown 处理)' : '否'}`;
StartupDiagnostics.lastAction = '已捕获有效冷启动 APP_LAUNCH'; StartupDiagnostics.lastAction = '已捕获有效冷启动 APP_LAUNCH';
StartupDiagnostics.logInfo(StartupDiagnostics.lastLaunchDetail); StartupDiagnostics.logInfo(StartupDiagnostics.lastLaunchDetail);
@@ -26,7 +26,7 @@ export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
try { try {
CrashDiagnostics.initialize(this.context as common.UIAbilityContext); CrashDiagnostics.initialize(this.context as common.UIAbilityContext);
StartupDiagnostics.initialize(); StartupDiagnostics.initialize(this.context as common.UIAbilityContext);
AuthSession.initialize(this.context as common.UIAbilityContext); AuthSession.initialize(this.context as common.UIAbilityContext);
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
} catch (err) { } catch (err) {