当前位置: 首页 > news >正文

HarmonyOS应用使用统计系统设计——数据埋点与成就系统实现

HarmonyOS应用使用统计系统设计——数据埋点与成就系统实现

技术栈:HarmonyOS 5.0 + ArkTS + Preferences

适用场景:用户行为分析、成就系统、数据可视化


前言

了解用户如何使用应用对产品迭代至关重要。本文将介绍如何在HarmonyOS应用中实现一个完整的使用统计系统,包括数据记录、统计分析和成就系统。

一、数据结构设计

1.1 使用记录

export interface UsageRecord {id: string;appId: string;      // 功能模块IDappName: string;    // 功能名称duration: number;   // 使用时长(秒)timestamp: number;  // 时间戳date: string;       // 日期 YYYY-MM-DD
}

1.2 统计数据

export interface StatisticsData {totalUseCount: number;    // 总使用次数totalDuration: number;    // 总使用时长todayUseCount: number;    // 今日使用次数todayDuration: number;    // 今日使用时长weeklyData: DailyData[];  // 周数据monthlyData: DailyData[]; // 月数据appUsage: AppUsageData[]; // 各功能使用统计achievements: Achievement[];
}export interface DailyData {date: string;useCount: number;duration: number;
}export interface AppUsageData {appId: string;appName: string;useCount: number;duration: number;
}

1.3 成就系统

export interface Achievement {id: string;name: string;description: string;icon: string;unlocked: boolean;unlockedAt?: number;requirement: number;  // 达成条件current: number;      // 当前进度
}

二、统计工具类实现

export class StatisticsUtil {private static readonly RECORDS_KEY = 'usage_records';private static readonly ACHIEVEMENTS_KEY = 'achievements';/*** 记录一次使用*/static async recordUsage(appId: string, appName: string, duration: number): Promise<void> {const now = Date.now();const date = new Date(now).toISOString().split('T')[0];const record: UsageRecord = {id: `${now}_${Math.random().toString(36).substr(2, 9)}`,appId,appName,duration,timestamp: now,date};const records = await StatisticsUtil.getRecords();records.push(record);// 只保留最近30天的记录const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1000;const filteredRecords = records.filter(r => r.timestamp > thirtyDaysAgo);await PreferencesUtil.putString(StatisticsUtil.RECORDS_KEY, JSON.stringify(filteredRecords));await StatisticsUtil.checkAchievements();}/*** 获取统计数据*/static async getStatistics(): Promise<StatisticsData> {const records = await StatisticsUtil.getRecords();const now = Date.now();const today = new Date(now).toISOString().split('T')[0];const weekAgo = now - 7 * 24 * 60 * 60 * 1000;// 总计const totalUseCount = records.length;const totalDuration = records.reduce((sum, r) => sum + r.duration, 0);// 今日const todayRecords = records.filter(r => r.date === today);const todayUseCount = todayRecords.length;const todayDuration = todayRecords.reduce((sum, r) => sum + r.duration, 0);// 周数据const weeklyData = StatisticsUtil.aggregateByDate(records.filter(r => r.timestamp > weekAgo));// 应用使用统计const appUsage = StatisticsUtil.aggregateByApp(records);// 成就const achievements = await StatisticsUtil.getAchievements(totalUseCount, totalDuration);return {totalUseCount,totalDuration,todayUseCount,todayDuration,weeklyData,monthlyData: [],appUsage,achievements};}/*** 按日期聚合*/private static aggregateByDate(records: UsageRecord[]): DailyData[] {const map = new Map<string, DailyData>();for (const record of records) {const existing = map.get(record.date);if (existing) {existing.useCount++;existing.duration += record.duration;} else {map.set(record.date, {date: record.date,useCount: 1,duration: record.duration});}}return Array.from(map.values()).sort((a, b) => a.date.localeCompare(b.date));}/*** 按应用聚合*/private static aggregateByApp(records: UsageRecord[]): AppUsageData[] {const map = new Map<string, AppUsageData>();for (const record of records) {const existing = map.get(record.appId);if (existing) {existing.useCount++;existing.duration += record.duration;} else {map.set(record.appId, {appId: record.appId,appName: record.appName,useCount: 1,duration: record.duration});}}return Array.from(map.values()).sort((a, b) => b.useCount - a.useCount);}
}

三、成就系统实现

private static readonly ACHIEVEMENT_DEFINITIONS: Achievement[] = [{ id: 'first_use', name: '初次体验', description: '完成第一次使用', icon: '🎉', requirement: 1, unlocked: false, current: 0 },{ id: 'use_10', name: '常客', description: '累计使用10次', icon: '⭐', requirement: 10, unlocked: false, current: 0 },{ id: 'use_50', name: '忠实用户', description: '累计使用50次', icon: '🏆', requirement: 50, unlocked: false, current: 0 },{ id: 'duration_1h', name: '时间投入', description: '累计使用1小时', icon: '⏰', requirement: 3600, unlocked: false, current: 0 },{ id: 'duration_10h', name: '深度用户', description: '累计使用10小时', icon: '💎', requirement: 36000, unlocked: false, current: 0 },
];static async checkAchievements(): Promise<void> {const records = await StatisticsUtil.getRecords();const totalCount = records.length;const totalDuration = records.reduce((sum, r) => sum + r.duration, 0);const achievements = await StatisticsUtil.loadAchievements();for (const achievement of achievements) {if (achievement.unlocked) continue;let current = 0;if (achievement.id.startsWith('use_') || achievement.id === 'first_use') {current = totalCount;} else if (achievement.id.startsWith('duration_')) {current = totalDuration;}achievement.current = current;if (current >= achievement.requirement) {achievement.unlocked = true;achievement.unlockedAt = Date.now();}}await PreferencesUtil.putString(StatisticsUtil.ACHIEVEMENTS_KEY, JSON.stringify(achievements));
}

四、页面展示

@Entry
@Component
struct StatisticsPage {@State statistics: StatisticsData | null = null;aboutToAppear(): void {this.loadStatistics();}async loadStatistics(): Promise<void> {this.statistics = await StatisticsUtil.getStatistics();}build() {Column() {// 总览卡片Row() {Column() {Text(`${this.statistics?.totalUseCount || 0}`).fontSize(32).fontWeight(FontWeight.Bold)Text('总使用次数').fontSize(12)}Column() {Text(this.formatDuration(this.statistics?.totalDuration || 0)).fontSize(32).fontWeight(FontWeight.Bold)Text('总使用时长').fontSize(12)}}// 成就列表Text('成就').fontSize(20).margin({ top: 20 })ForEach(this.statistics?.achievements || [], (item: Achievement) => {Row() {Text(item.icon).fontSize(24)Column() {Text(item.name).fontWeight(FontWeight.Bold)Text(item.description).fontSize(12)}Text(item.unlocked ? '✓' : `${item.current}/${item.requirement}`)}})}}formatDuration(seconds: number): string {if (seconds < 60) return `${seconds}秒`;if (seconds < 3600) return `${Math.floor(seconds / 60)}分`;return `${Math.floor(seconds / 3600)}时`;}
}

五、避坑指南

  1. 数据清理:定期清理过期数据,避免存储膨胀
  2. 异步处理:统计操作不要阻塞主线程
  3. 隐私保护:所有数据仅存储在本地
  4. 性能优化:大量数据时考虑分页加载

总结

本文实现了一个完整的应用使用统计系统,包括数据记录、统计分析和成就系统。这些数据可以帮助开发者了解用户行为,同时成就系统也能提升用户粘性。

http://www.jsqmd.com/news/109783/

相关文章:

  • EmotiVoice语音合成引擎的国际化部署建议
  • 2025 AGV十大品牌解析:重载与无人化双线爆发,AGV 行业进入 “硬核创新” 时代 - 品牌推荐排行榜
  • C#实现的全能HTTP POST工具类
  • 63、活动目录安全、认证、日志记录、监控与配额管理指南
  • ICS 期末复习卷 2
  • HarmonyOS深色模式适配实战——主题切换与WCAG对比度标准
  • 运维---做些什么?架构
  • 2025年年终上海管道疏通推荐:权威榜单解析与专业服务对比评测 - 十大品牌推荐
  • 2025年女孩起名机构联系方式汇总:全国知名机构官方联系通道与专业选择指南 - 十大品牌推荐
  • 76、深入解析AD LDS访问审计与AD FS服务配置
  • 2025年年终上海管道疏通推荐:专业服务排行与用户口碑对比分析 - 十大品牌推荐
  • 6-9 WPS JS宏Map、 set、get、delete、clear()映射的添加、修改、删除
  • 互联网大厂Java面试故事:从Spring全家桶到AI应用场景深度剖析
  • 国产化Excel开发组件Spire.XLS教程:以Python编程方式在Excel中高亮重复值
  • 11、僵尸网络检测:工具与技术全解析
  • 聚焦2025:十大备受推崇的BIP管理平台横向评测,好生意/好会计/易代账/制造云/好业财/协同云/供应链云/人力云BIP服务商推荐排行榜 - 品牌推荐师
  • 2025年12月包头钢结构/钢结构厂房/建筑钢结构/厂家深度分析 - 2025年品牌推荐榜
  • 政府机构适用吗?Kotaemon满足等保三级要求
  • 12、Ourmon:网络监控与异常检测工具全解析
  • 2025上海新房装修指南:十家靠谱装修公司盘点,助你打造理想新家 - 速递信息
  • 2025年装修必看:五大靠谱现浇楼梯公司评选报告,楼板搭建/现浇钢筋混凝土/现浇楼板/现浇楼梯/现浇楼梯公司哪家好选哪家 - 品牌推荐师
  • 一文搞懂RAG架构演进史,以及Kotaemon的创新突破
  • Claude code 多种模型随时切换(最简单的方法)【hepingfly】
  • GPT-5.2并非全面升级,OneEval V1.3 最新“LLM+KB”评测结果出炉
  • 图像重建中的软阈值方法
  • 2025年质量好的变风量阀最新TOP厂家排名 - 行业平台推荐
  • 2025年比较好的工业搅拌器/高粘度物料搅拌器厂家最新实力排行 - 行业平台推荐
  • JavaSE——JRE和JDK
  • 基于微信小程序的在线家庭园艺系统毕业设计源码
  • 腾讯混元这次开源太狠了!把AI视频变成了用户可操控的“开放世界”。(附实操体验)