HarmonyOS开发:用户分析——用户行为分析
HarmonyOS开发:用户分析——用户行为分析
📌 核心要点:用户行为分析是把"用户到底在干嘛"这件事从混沌变成清晰——构建用户画像、量化活跃度、精准分群,让产品决策从"我觉得"变成"数据说"。
背景与动机
你有没有遇到过这种场景:产品经理说"我们的用户都喜欢用搜索功能",运营说"不对,用户都是刷推荐流的",开发说"你们都别吵了,我看代码里搜索的调用量比推荐高"。
三个人三种说法,谁对?都不对。因为"喜欢"是个模糊概念,你得用数据说话——搜索渗透率多少?推荐流点击率多少?用户从搜索进来的转化率比推荐高还是低?
用户行为分析要解决的核心问题:
- 用户是谁:用户画像,给每个用户打上标签,知道你的用户长什么样
- 用户活跃吗:DAU、MAU、活跃度分层,别把"装了就忘"的用户也算活跃
- 用户怎么用:使用路径、功能偏好、停留时长,用户到底在用你的什么
- 用户怎么分:分群策略,不同用户用不同策略,别一锅炖
鸿蒙应用的用户行为分析有其特殊性——分布式场景下用户可能跨设备使用,同一个用户在手机上浏览、在平板上购买,你得把跨设备的行为串起来。
核心原理
用户行为分析的架构:行为采集 → 行为建模 → 画像构建 → 分群分析 → 策略输出。
flowchart TB
subgraph 采集层
A[页面浏览事件]
B[功能点击事件]
C[业务转化事件]
D[用户属性数据]
end
subgraph 建模层
E[行为序列构建]
F[会话识别]
G[跨设备归因]
end
subgraph 画像层
H[基础画像<br/>年龄/地区/设备]
I[行为画像<br/>偏好/频次/深度]
J[价值画像<br/>付费/活跃/留存]
end
subgraph 分群层
K[RFM分群]
L[生命周期分群]
M[行为聚类分群]
end
subgraph 输出层
N[精准推送]
O[个性化推荐]
P[运营策略]
end
A --> E
B --> E
C --> E
D --> H
E --> F --> G
G --> H
G --> I
G --> J
H --> K
I --> K
J --> K
H --> L
I --> L
J --> M
K --> N
L --> O
M --> P
classDef collect fill:#E17055,stroke:#D63031,color:#fff
classDef model fill:#FDCB6E,stroke:#F0B429,color:#333
classDef profile fill:#74B9FF,stroke:#0984E3,color:#fff
classDef segment fill:#A29BFE,stroke:#6C5CE7,color:#fff
classDef output fill:#55EFC4,stroke:#00B894,color:#333
class A,B,C,D collect
class E,F,G model
class H,I,J profile
class K,L,M segment
class N,O,P output
用户行为分析的核心模型:
| 模型 | 说明 | 核心指标 |
|---|---|---|
| 用户画像 | 给用户打标签,描述用户特征 | 标签覆盖率、画像完整度 |
| 活跃度分析 | 衡量用户使用频率和深度 | DAU、MAU、DAU/MAU比 |
| 参与度分析 | 衡量用户使用质量和粘性 | 会话时长、页面深度、功能使用率 |
| 用户分群 | 按特征将用户分组 | 分群规模、群间差异度 |
| RFM模型 | 按最近消费、频次、金额分层 | R值、F值、M值 |
代码实战
基础用法:用户画像构建
先给用户打标签,这是所有分析的基础。
// UserProfileManager.ets - 用户画像管理
import { analytics } from '@kit.AnalyticsKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 用户画像标签定义
export enum UserTag {
// 基础标签
USER_ID = 'user_id',
REGISTER_CHANNEL = 'register_channel',
USER_TYPE = 'user_type',
CITY = 'city',
DEVICE_TYPE = 'device_type',
// 行为标签
LAST_VISIT_TIME = 'last_visit_time',
VISIT_COUNT_7D = 'visit_count_7d',
PREFERRED_CATEGORY = 'preferred_category',
AVG_SESSION_DURATION = 'avg_session_duration',
// 价值标签
IS_PAID_USER = 'is_paid_user',
TOTAL_PAYMENT = 'total_payment',
VIP_LEVEL = 'vip_level',
LTV = 'ltv',
}
export class UserProfileManager {
private static instance: UserProfileManager;
private profileCache: Map<string, string> = new Map();
static getInstance(): UserProfileManager {
if (!UserProfileManager.instance) {
UserProfileManager.instance = new UserProfileManager();
}
return UserProfileManager.instance;
}
// 设置单个标签
setTag(tag: UserTag, value: string): void {
try {
analytics.setUserProfile(tag, value);
this.profileCache.set(tag, value);
console.info(`[UserProfile] 标签设置: ${tag} = ${value}`);
} catch (error) {
const err = error as BusinessError;
console.error(`[UserProfile] 设置标签失败: ${err.code}`);
}
}
// 批量设置标签
setTags(tags: Record<string, string>): void {
Object.entries(tags).forEach(([key, value]) => {
this.setTag(key as UserTag, value);
});
}
// 获取缓存标签
getTag(tag: UserTag): string | undefined {
return this.profileCache.get(tag);
}
// 更新用户访问信息(每次启动调用)
updateVisitInfo(): void {
const now = Date.now();
const lastVisit = this.profileCache.get(UserTag.LAST_VISIT_TIME);
// 更新最后访问时间
this.setTag(UserTag.LAST_VISIT_TIME, now.toString());
// 计算7天内访问次数
let visitCount = parseInt(this.profileCache.get(UserTag.VISIT_COUNT_7D) || '0');
visitCount++;
this.setTag(UserTag.VISIT_COUNT_7D, visitCount.toString());
}
// 更新付费信息
updatePaymentInfo(amount: number): void {
// 标记为付费用户
this.setTag(UserTag.IS_PAID_USER, 'true');
// 累计付费金额
let totalPayment = parseFloat(this.profileCache.get(UserTag.TOTAL_PAYMENT) || '0');
totalPayment += amount;
this.setTag(UserTag.TOTAL_PAYMENT, totalPayment.toFixed(2));
// 更新VIP等级
const vipLevel = this.calculateVipLevel(totalPayment);
this.setTag(UserTag.VIP_LEVEL, vipLevel.toString());
}
// 计算VIP等级
private calculateVipLevel(totalPayment: number): number {
if (totalPayment >= 1000) return 5;
if (totalPayment >= 500) return 4;
if (totalPayment >= 200) return 3;
if (totalPayment >= 50) return 2;
if (totalPayment > 0) return 1;
return 0;
}
}
进阶用法:活跃度与参与度分析
光有画像不够,你还得知道用户活不活跃、参不参与。
// ActivityAnalyzer.ets - 活跃度分析器
import { analytics } from '@kit.AnalyticsKit';
import { preferences } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
// 活跃度等级
export enum ActivityLevel {
HIGH = 'high', // 高活跃:近7天访问≥5天
MEDIUM = 'medium', // 中活跃:近7天访问2-4天
LOW = 'low', // 低活跃:近7天访问1天
SILENT = 'silent', // 沉默:近7天未访问
}
// 参与度指标
export interface EngagementMetrics {
sessionCount: number; // 会话数
avgSessionDuration: number; // 平均会话时长(秒)
pageDepth: number; // 页面深度
featureUsageRate: number; // 核心功能使用率
interactionCount: number; // 交互次数
}
export class ActivityAnalyzer {
private static instance: ActivityAnalyzer;
private context: common.UIAbilityContext | null = null;
private pref: preferences.Preferences | null = null;
// 会话追踪
private sessionStartTime: number = 0;
private sessionPageCount: number = 0;
private sessionInteractionCount: number = 0;
static getInstance(): ActivityAnalyzer {
if (!ActivityAnalyzer.instance) {
ActivityAnalyzer.instance = new ActivityAnalyzer();
}
return ActivityAnalyzer.instance;
}
// 初始化
async init(context: common.UIAbilityContext): Promise<void> {
this.context = context;
try {
this.pref = await preferences.getPreferences(context, 'activity_data');
} catch (error) {
console.error('[ActivityAnalyzer] 初始化失败');
}
}
// 开始新会话
startSession(): void {
this.sessionStartTime = Date.now();
this.sessionPageCount = 0;
this.sessionInteractionCount = 0;
// 记录今日已访问
this.recordVisitDay();
console.info('[ActivityAnalyzer] 会话开始');
}
// 结束会话
endSession(): void {
if (this.sessionStartTime === 0) return;
const duration = (Date.now() - this.sessionStartTime) / 1000; // 秒
const metrics: EngagementMetrics = {
sessionCount: 1,
avgSessionDuration: duration,
pageDepth: this.sessionPageCount,
featureUsageRate: 0, // 需要结合业务计算
interactionCount: this.sessionInteractionCount,
};
// 上报会话数据
analytics.reportEvent('session_end', {
'duration': duration,
'page_depth': this.sessionPageCount,
'interaction_count': this.sessionInteractionCount,
});
// 更新用户活跃度标签
this.updateActivityLevel();
// 重置会话数据
this.sessionStartTime = 0;
this.sessionPageCount = 0;
this.sessionInteractionCount = 0;
}
// 记录页面浏览
onPageView(pageName: string): void {
this.sessionPageCount++;
analytics.reportEvent('page_view', {
'page_name': pageName,
'session_page_index': this.sessionPageCount,
});
}
// 记录用户交互
onInteraction(action: string, target: string): void {
this.sessionInteractionCount++;
analytics.reportEvent('user_interaction', {
'action': action,
'target': target,
});
}
// 记录访问日期
private async recordVisitDay(): Promise<void> {
if (!this.pref) return;
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
const visitDaysStr = await this.pref.get('visit_days_7d', '[]') as string;
const visitDays: string[] = JSON.parse(visitDaysStr);
// 去重添加今日
if (!visitDays.includes(today)) {
visitDays.push(today);
}
// 只保留近7天
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const filteredDays = visitDays.filter(d => new Date(d) >= sevenDaysAgo);
await this.pref.put('visit_days_7d', JSON.stringify(filteredDays));
await this.pref.flush();
}
// 计算活跃度等级
async getActivityLevel(): Promise<ActivityLevel> {
if (!this.pref) return ActivityLevel.SILENT;
const visitDaysStr = await this.pref.get('visit_days_7d', '[]') as string;
const visitDays: string[] = JSON.parse(visitDaysStr);
const count = visitDays.length;
if (count >= 5) return ActivityLevel.HIGH;
if (count >= 2) return ActivityLevel.MEDIUM;
if (count >= 1) return ActivityLevel.LOW;
return ActivityLevel.SILENT;
}
// 更新活跃度标签
private async updateActivityLevel(): Promise<void> {
const level = await this.getActivityLevel();
analytics.setUserProfile('activity_level', level);
}
}
完整示例:用户分群策略
有了画像和活跃度,就可以做用户分群了。不同用户不同策略,这才叫精细化运营。
// UserSegmentation.ets - 用户分群策略
import { analytics } from '@kit.AnalyticsKit';
import { preferences } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { ActivityLevel, ActivityAnalyzer } from './ActivityAnalyzer';
// 分群类型
export enum SegmentType {
NEW_USER = 'new_user', // 新用户:注册7天内
ACTIVE_USER = 'active_user', // 活跃用户:高活跃+有付费
POTENTIAL_USER = 'potential_user', // 潜力用户:高活跃+未付费
CHURN_RISK = 'churn_risk', // 流失风险:中活跃下降趋势
CHURNED = 'churned', // 已流失:沉默用户
WHALE = 'whale', // 大R用户:付费金额Top5%
}
// 分群结果
export interface SegmentResult {
segment: SegmentType;
confidence: number; // 置信度 0-1
traits: string[]; // 特征描述
strategy: string; // 推荐策略
}
export class UserSegmentation {
private static instance: UserSegmentation;
private pref: preferences.Preferences | null = null;
private currentSegment: SegmentType | null = null;
static getInstance(): UserSegmentation {
if (!UserSegmentation.instance) {
UserSegmentation.instance = new UserSegmentation();
}
return UserSegmentation.instance;
}
async init(context: common.UIAbilityContext): Promise<void> {
this.pref = await preferences.getPreferences(context, 'segment_data');
}
// 执行分群——核心方法
async segmentUser(): Promise<SegmentResult> {
const activityLevel = await ActivityAnalyzer.getInstance().getActivityLevel();
const isPaidUser = await this.getIsPaidUser();
const registerDays = await this.getRegisterDays();
const totalPayment = await this.getTotalPayment();
const visitTrend = await this.getVisitTrend();
let result: SegmentResult;
// 分群逻辑——优先级从高到低
if (registerDays <= 7) {
// 新用户:注册7天内
result = {
segment: SegmentType.NEW_USER,
confidence: 0.95,
traits: ['注册时间短', '行为模式未定型', '需要引导'],
strategy: '新手引导+首单优惠+功能推荐',
};
} else if (totalPayment >= 500) {
// 大R用户:累计付费>=500
result = {
segment: SegmentType.WHALE,
confidence: 0.9,
traits: ['高付费', '高忠诚度', '追求品质'],
strategy: '专属客服+优先体验+VIP特权',
};
} else if (activityLevel === ActivityLevel.HIGH && isPaidUser) {
// 活跃付费用户
result = {
segment: SegmentType.ACTIVE_USER,
confidence: 0.85,
traits: ['高活跃', '已付费', '功能深度使用'],
strategy: '会员续费+交叉推荐+社区互动',
};
} else if (activityLevel === ActivityLevel.HIGH && !isPaidUser) {
// 活跃未付费——潜力用户
result = {
segment: SegmentType.POTENTIAL_USER,
confidence: 0.8,
traits: ['高活跃', '未付费', '使用免费功能'],
strategy: '限时优惠+付费功能试用+价值展示',
};
} else if (activityLevel === ActivityLevel.MEDIUM && visitTrend === 'declining') {
// 中活跃但下降——流失风险
result = {
segment: SegmentType.CHURN_RISK,
confidence: 0.7,
traits: ['活跃度下降', '使用频率减少', '可能流失'],
strategy: '召回推送+专属优惠+功能更新通知',
};
} else if (activityLevel === ActivityLevel.SILENT || activityLevel === ActivityLevel.LOW) {
// 已流失
result = {
segment: SegmentType.CHURNED,
confidence: 0.75,
traits: ['长期未访问', '可能已卸载', '需要强召回'],
strategy: '短信召回+大额优惠券+新功能亮点推送',
};
} else {
// 默认归为潜力用户
result = {
segment: SegmentType.POTENTIAL_USER,
confidence: 0.5,
traits: ['中等活跃', '行为待观察'],
strategy: '持续观察+轻度运营',
};
}
// 保存分群结果
this.currentSegment = result.segment;
analytics.setUserProfile('user_segment', result.segment);
analytics.setUserProfile('segment_confidence', result.confidence.toString());
// 上报分群事件
analytics.reportEvent('user_segmented', {
'segment': result.segment,
'confidence': result.confidence,
'activity_level': activityLevel,
'is_paid': isPaidUser,
'register_days': registerDays,
});
return result;
}
// 获取当前分群
getCurrentSegment(): SegmentType | null {
return this.currentSegment;
}
// ========== 辅助方法 ==========
private async getIsPaidUser(): Promise<boolean> {
if (!this.pref) return false;
return (await this.pref.get('is_paid_user', false)) as boolean;
}
private async getRegisterDays(): Promise<number> {
if (!this.pref) return 999;
const registerTime = (await this.pref.get('register_time', 0)) as number;
if (registerTime === 0) return 999;
return Math.floor((Date.now() - registerTime) / (1000 * 60 * 60 * 24));
}
private async getTotalPayment(): Promise<number> {
if (!this.pref) return 0;
return (await this.pref.get('total_payment', 0)) as number;
}
private async getVisitTrend(): Promise<string> {
if (!this.pref) return 'stable';
return (await this.pref.get('visit_trend', 'stable')) as string;
}
}
分群结果的使用——不同用户展示不同首页:
// HomePage.ets - 根据分群展示不同首页
import { UserSegmentation, SegmentType } from '../manager/UserSegmentation';
@Entry
@Component
struct HomePage {
@State currentSegment: SegmentType = SegmentType.NEW_USER;
async aboutToAppear() {
const segmentation = UserSegmentation.getInstance();
const result = await segmentation.segmentUser();
this.currentSegment = result.segment;
}
build() {
Column() {
// 根据分群展示不同内容
if (this.currentSegment === SegmentType.NEW_USER) {
this.NewUserHome()
} else if (this.currentSegment === SegmentType.WHALE) {
this.WhaleUserHome()
} else if (this.currentSegment === SegmentType.POTENTIAL_USER) {
this.PotentialUserHome()
} else {
this.DefaultHome()
}
}
.width('100%')
.height('100%')
}
@Builder NewUserHome() {
// 新手引导+首单优惠
Column() {
Text('👋 欢迎来到鸿蒙商城')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text('新用户专享:首单立减20元')
.fontSize(16)
.fontColor('#FF6B6B')
.margin({ top: 12 })
// 新手引导步骤
this.GuideSteps()
}
}
@Builder WhaleUserHome() {
// VIP专属
Column() {
Text('👑 尊享会员专区')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text('专属客服 · 优先体验 · 极速发货')
.fontSize(14)
.fontColor('#8B5CF6')
.margin({ top: 8 })
}
}
@Builder PotentialUserHome() {
// 限时优惠引导付费
Column() {
Text('🔥 限时特惠')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text('开通会员立享7天免费体验')
.fontSize(16)
.fontColor('#F59E0B')
.margin({ top: 12 })
}
}
@Builder DefaultHome() {
Text('首页内容')
}
@Builder GuideSteps() {
// 新手引导步骤组件
Text('1️⃣ 浏览商品 → 2️⃣ 加入购物车 → 3️⃣ 完成首单')
.fontSize(14)
.margin({ top: 16 })
}
}
踩坑与注意事项
1. 画像标签不是越多越好
有些团队一上来就给用户打100个标签,结果呢?标签之间互相矛盾,分析的时候不知道看哪个。标签要精不要多,核心标签控制在20个以内,每个标签都要有明确的业务含义和使用场景。
2. 活跃度定义要贴合业务
DAU/MAU这个比值,不同类型的应用差异巨大。社交应用可能DAU/MAU > 50%,工具类应用可能只有10%。别拿社交应用的标准去要求工具类应用,那只会让团队做无用功。
3. 分群策略要定期更新
用户是会变的——今天的潜力用户,明天可能就变成活跃用户了。分群策略至少每月更新一次,关键分群(如流失风险)每周更新。
4. 跨设备用户识别
鸿蒙分布式场景下,同一个华为账号可能登录多个设备。你得把跨设备的行为串起来,否则同一个用户在手机上算一个,在平板上又算一个,数据就乱了。
// 跨设备用户识别
import { distributedDeviceManager } from '@kit.DistributedDeviceManager';
// 用华为账号ID作为用户唯一标识
async function getUnifiedUserId(): Promise<string> {
// 优先使用华为账号ID
const accountId = await getHuaweiAccountId();
if (accountId) {
return `hw_${accountId}`;
}
// 降级使用设备ID + 本地生成的UUID
const deviceId = getDeviceId();
const localUUID = await getLocalUUID();
return `local_${deviceId}_${localUUID}`;
}
5. 隐私合规
用户画像涉及大量个人数据,必须遵守隐私法规。采集前告知用户、获取同意,敏感标签要脱敏处理,用户有权删除自己的画像数据。
HarmonyOS 6适配说明
HarmonyOS 6在用户分析方面有几项重要更新:
- 跨设备画像同步:通过华为账号体系,用户画像可跨设备自动同步,无需手动处理
- 隐私计算增强:新增
on-device ML能力,部分画像计算可在本地完成,不上传原始数据 - 实时分群:分群计算从离线批处理升级为实时流式计算,用户行为变化后秒级更新分群
- 分群API标准化:新增
analytics.getSegment()标准接口,统一分群查询方式
// HarmonyOS 6 实时分群查询
async function getUserSegment(): Promise<string> {
try {
const segment = await analytics.getSegment();
console.info(`[Analytics] 当前分群: ${segment}`);
return segment;
} catch (error) {
console.error('[Analytics] 分群查询失败');
return 'unknown';
}
}
总结
用户行为分析是产品优化的指南针——没有分析,你不知道用户是谁、在干什么、会不会走。用户画像告诉你"用户长什么样",活跃度分析告诉你"用户活不活跃",分群策略告诉你"不同用户该怎么对待"。
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐☆☆ SDK简单,分析模型设计有门槛 |
| 使用频率 | ⭐⭐⭐⭐⭐ 持续使用,每日更新 |
| 重要程度 | ⭐⭐⭐⭐⭐ 直接影响产品决策和运营效果 |
核心记住三点:画像要精不要多、活跃度定义要贴合业务、分群要定期更新。别搞了100个标签结果一个都用不上,那不叫用户画像,那叫数据垃圾场。
- 点赞
- 收藏
- 关注作者
评论(0)