Files
51dj_android/analytics_sdk/SDK_USAGE_GUIDE.md
T
2026-10-08 11:37:25 +07:00

32 KiB
Raw Blame History

Analytics SDK 使用文档

目录

  1. 概述
  2. 安装
  3. SDK 初始化
  4. 用户管理
  5. 自动埋点
  6. 手动事件上报
  7. 枚举参考
  8. SdkConfig 全局参数
  9. 上报数据格式
  10. 调试工具
  11. 高级用法
  12. 完整集成示例

1. 概述

Analytics SDK 是一个 Flutter 插件,提供:

  • 自动埋点:页面浏览、页面生命周期、全局点击、导航事件(均默认开启)
  • 手动埋点:视频/小说/漫画行为、广告、推荐等客户端事件
  • 本地持久化缓存(JSONL 格式),断网重传
  • 域名并发测速,自动选择最快上报节点
  • AES-GCM 加密配置,防止配置明文泄露
  • 事件类型白名单控制(服务端下发)
  • 广告展示去重(session 级别)
  • 设备信息自动采集(通过 device_info_plus,无需外部传入)
  • 设备指纹自动生成
  • getParams() 方法暴露全部或指定公共参数,供服务端复用

所有公开方法均不会向外抛出异常,SDK 内部失败不影响宿主应用运行。


2. 安装

在 pubspec.yaml 中添加依赖:

dependencies:
  analytics_sdk:
    path: ../analytics_sdk  # 或 git/pub 地址

运行:

flutter pub get

3. SDK 初始化

3.1 最简初始化

import 'package:analytics_sdk/analytics_sdk.dart';

await AnalyticsSdk.instance.init(
  appId: 'your_app_id',
  encryptedConfig: 'Base64加密字符串',  // 服务端下发
  deviceId: 'your_device_id',           // Android IMEI/OAID、iOS IDFV 等
);

设备品牌、型号、系统名称、版本等信息由 SDK 内部通过 device_info_plus 自动采集。

3.2 完整参数说明

参数 类型 必填 默认值 说明
appId String 是 — 应用 ID
encryptedConfig String? 是(可传 null) null 加密配置字符串,见下方格式说明
channel String? 否 '' 渠道标识,可后续通过 setChannel 更新
uid String? 否 '' 用户 ID,未登录可传空,登录后通过 setUserIdAndType 更新
deviceId String 是 — 设备唯一标识(Android IMEI/OAID、iOS IDFV 等)
enableDebugBanner bool 否 false 是否显示调试悬浮条(仅在非 release 包生效)

自动采集字段:device(类型)、device_brand、device_model、system_name、system_version、user_agent 均由 SDK 通过 device_info_plus 自动读取,无需传入。

3.3 encryptedConfig 格式说明

encryptedConfig 是一个 Base64 编码的 AES-GCM 密文,解密后内容为:

{
  "domainList": [
    "https://api1.example.com",
    "https://api2.example.com"
  ],
  "eventList": [
    "app_page_view",
    "page_click",
    "video_event",
    "ad_impression"
  ]
}
  • domainList:上报域名列表,SDK 启动时会并发测速,选择最快的域名上报。
  • eventList:启用的事件类型白名单。不在列表中的事件会被静默丢弃。传空列表或省略此字段时,所有事件类型均允许上报。

如需在调试包中验证加密串,使用 validateEncryptedConfig。

3.4 初始化时机

建议在 main() 中 runApp() 之前调用:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 可选:在 init() 前覆盖默认参数
  SdkConfig.configure(
    uploadInterval: const Duration(seconds: 5),
    autoUploadThreshold: 10,
  );

  await AnalyticsSdk.instance.init(
    appId: 'your_app_id',
    encryptedConfig: await fetchEncryptedConfigFromServer(),
    deviceId: await getDeviceId(),
    channel: 'google_play',
    uid: currentUser?.id ?? '',
  );

  runApp(const MyApp());
}

4. 用户管理

4.1 登录 / 切换账号

AnalyticsSdk.setUserIdAndType(
  userId: '12345',
  userTypeEnum: UserTypeEnum.vip,
);
参数 类型 说明
userId String 用户 ID,默认空字符串(未登录状态)
userTypeEnum UserTypeEnum? 用户类型,null 时默认为 normal

4.2 仅更新 uid(不改变用户类型)

AnalyticsSdk.setUid('12345');

4.3 更新渠道

AnalyticsSdk.setChannel('huawei_store');

4.4 登出

AnalyticsSdk.logoutUser();

5. 自动埋点

5.1 页面生命周期自动追踪(PageLifecycleObserver)

MaterialApp(
  navigatorObservers: [
    AnalyticsSdk.instance.pageObserver,
  ],
)

注册后 SDK 会自动追踪页面进入、离开和导航事件,无需手动上报。

获取当前页面 key:

final currentPage = PageLifecycleObserver.currentPageKey;

5.2 页面名称映射(PageNameMapper)

import 'package:analytics_sdk/manager/page_name_manager.dart';

PageNameMapper.addMappings({
  'home': '首页',
  'video_detail': '视频详情页',
  'user_center': '个人中心',
  'search': '搜索页',
});

final name = PageNameMapper.getPageName('video_detail'); // '视频详情页'

注意:pageKey 和映射 key 均会自动去除前导 /,传 '/video_detail' 与 'video_detail' 效果相同。

5.3 全局点击自动追踪(GlobalClickWrapper)

import 'package:analytics_sdk/widget/global_click_wrapper.dart';

MaterialApp(
  builder: (context, child) => GlobalClickWrapper(child: child!),
)

自动上报 PageClickEvent,含归一化坐标(300ms 节流)。


6. 手动事件上报

所有手动事件通过 AnalyticsSdk.instance.track(event) 上报。

6.1 App 安装事件【客户端】

触发时机:首次安装启动后,读取剪贴板获取到 traceId 时上报。

AnalyticsSdk.instance.track(AppInstallEvent(
  traceId: 'clipboard_trace_id',
));
字段 类型 说明
traceId String 落地页生成并写入剪贴板的唯一追踪 ID

6.2 视频行为事件【客户端】

触发时机:用户播放、暂停、快进、完播等操作时上报。

AnalyticsSdk.instance.track(VideoEvent(
  videoId: 'vid_001',
  videoTitle: '精彩大片',
  videoTypeId: 'action',
  videoTypeName: '动作片',
  videoTagKey: 'hot,new',
  videoTagName: '热门,最新',
  videoDuration: 7200,
  playDuration: 120,
  playProgress: 30,
  videoBehavior: VideoEventEnum.VIDEO_PLAY,
  videoContentType: VideoContentTypeEnum.video,
  recommendTraceId: 'trace_abc123',  // 未接推荐引擎传 ''
  mediaId: '',
));
字段 类型 必填 说明
videoId String 是 视频 ID
videoTitle String 是 视频标题
videoTypeId String 是 视频分类 ID
videoTypeName String 是 视频分类名称
videoTagKey String 是 标签 KEY,多个用英文逗号分隔
videoTagName String 是 标签名称,多个用英文逗号分隔
videoDuration int 是 视频总时长(秒)
playDuration int 是 本次播放时长(秒)
playProgress int 是 播放进度百分比(0-100)
videoBehavior VideoEventEnum 是 视频行为,见 VideoEventEnum
videoContentType VideoContentTypeEnum? 否 内容类型:video(长视频) / short_video(短视频),未传时上报空字符串
recommendTraceId String 否 推荐引擎 trace ID,未接推荐引擎传 '',默认 ''
mediaId String 否 媒体资源 ID,默认 ''

6.3 小说阅读事件【客户端】

AnalyticsSdk.instance.track(NovelEvent(
  novelId: 'novel_001',
  novelTitle: '斗破苍穹',
  novelTypeId: 'fantasy',
  novelTypeName: '玄幻',
  novelTagKey: 'hot,classic',
  novelTagName: '热门,经典',
  readProgress: 45,
  pageNo: 10,
  novelBehavior: ReadBehaviorEnum.PAGE_NEXT,
  mediaId: '',
));
字段 类型 必填 说明
novelId String 是 小说 ID
novelTitle String 是 小说标题
novelTypeId String 是 分类 ID
novelTypeName String 是 分类名称
novelTagKey String 是 标签 KEY,多个用英文逗号分隔
novelTagName String 是 标签名称,多个用英文逗号分隔
readProgress int 是 阅读进度百分比(0-100)
pageNo int 是 当前阅读第几页(从 1 开始)
novelBehavior ReadBehaviorEnum 是 阅读行为,见 ReadBehaviorEnum
recommendTraceId String 否 推荐引擎 trace ID,未接推荐引擎传 '',默认 ''
mediaId String 否 媒体资源 ID,未使用老司机库资源时传 ''

6.4 漫画阅读事件【客户端】

AnalyticsSdk.instance.track(ComicEvent(
  comicId: 'comic_001',
  comicTitle: '火影忍者',
  comicTypeId: 'battle',
  comicTypeName: '热血',
  comicTagKey: 'hot,classic',
  comicTagName: '热门,经典',
  readProgress: 60,
  pageNo: 5,
  comicBehavior: ReadBehaviorEnum.PAGE_NEXT,
));

字段与 NovelEvent 对称,将 novel* 前缀替换为 comic*,novelBehavior 替换为 comicBehavior。recommendTraceId 字段同样支持,默认 ''。


6.5 广告展示事件【客户端】(带自动去重)

SDK 会对 adId 进行 session 级别去重:同一个 adId 在当前 session 内只会上报一次。

AnalyticsSdk.instance.track(AdImpressionEvent(
  pageKey: 'home',
  pageName: '首页',
  adSlotKey: 'home_banner_1',
  adSlotName: '首页顶部Banner',
  adId: 'ad_001,ad_002,ad_003',
  creativeId: 'cr_001,cr_002,cr_003',
  adType: 'banner',   // 传广告类型字符串,不传默认空字符串
));
字段 类型 必填 说明
pageKey String 是 页面标识
pageName String 是 页面名称
adSlotKey String 是 广告位标识
adSlotName String 是 广告位名称
adId String 是 广告 ID,多个用英文逗号分隔
creativeId String 否 素材 ID,多个用英文逗号分隔,与 adId 一一对应,默认 ''
adType String 否 广告类型字符串,如 'banner'、'feed'、'splash',默认 ''

去重逻辑:

  • 进入 TabA 展示 20 条广告 → 上报 20 条
  • 切换到 TabB 展示 20 条广告 → 上报 20 条
  • 返回 TabA 上滑新加载 10 条广告 → 上报新的 10 条
  • 再次下滑看到之前已展示的广告 → 全部被过滤

6.6 广告点击事件【客户端】

AnalyticsSdk.instance.track(AdClickEvent(
  pageKey: 'home',
  pageName: '首页',
  adSlotKey: 'home_banner_1',
  adSlotName: '首页顶部Banner',
  adId: 'ad_001',
  creativeId: 'cr_001',
  adType: 'banner',
));

字段与 AdImpressionEvent 相同,adId 为单个广告 ID。


6.7 APP 广告行为事件【客户端】(弹窗/Banner 行为)

用于上报 APP 内置广告(弹窗、Banner 等)的展示、点击、关闭行为。

AnalyticsSdk.instance.track(AdvertisingEvent(
  eventType: BannerEventEnum.CLICK.label,
  advertisingKey: 'home_popup',
  advertisingName: '首页弹窗',
  advertisingId: 'adv_001',
));
字段 类型 说明
eventType String 行为类型:click/close/show
advertisingKey String 广告标识
advertisingName String 广告名称
advertisingId String 广告 ID

6.8 关键词搜索事件【客户端】

AnalyticsSdk.instance.track(KeywordSearchEvent(
  keyword: '火影忍者',
  searchResultCount: 42,
  searchTraceId: '',  // 未接搜索引擎时传空字符串
  searchId: '',       // 未接搜索引擎时传空字符串
));
字段 类型 必填 说明
keyword String 是 搜索关键词
searchResultCount int 是 搜索结果数量
searchTraceId String 否 搜索引擎 trace ID,未接搜索引擎时传 ''
searchId String 否 搜索引擎 search ID,未接搜索引擎时传 ''

6.9 关键词点击事件【客户端】

AnalyticsSdk.instance.track(KeywordClickEvent(
  keyword: '火影忍者',
  clickItemId: 'vid_001',
  clickItemType: ClickItemTypeEnum.VIDEO,
  clickPosition: 1,
  searchTraceId: '',  // 未接搜索引擎时传空字符串
));
字段 类型 必填 说明
keyword String 是 搜索关键词
clickItemId String 是 点击项目 ID(视频ID / 小说ID 等)
clickItemType ClickItemTypeEnum 是 点击项目类型:VIDEO / NOVEL / COMIC
clickPosition int 是 点击位置(搜索结果中的排序位置,从 1 开始)
searchTraceId String 否 搜索引擎 trace ID,未接搜索引擎时传 ''

6.10 推荐列表展示事件【客户端】

AnalyticsSdk.instance.track(RecommendListViewEvent(
  pageKey: 'home',
  pageName: '首页',
  recommendContentType: RecommendContentTypeEnum.video,
  recommendTraceId: 'rec_trace_abc123',
  recommendId: 'vid_001,vid_002,vid_003',
  recommendTraceInfo: '{"source":"server"}',  // 未接推荐引擎传 ''
  clientVersion: '1.2.3',                     // 未接推荐引擎传 ''
));
字段 类型 必填 说明
pageKey String 是 页面标识
pageName String 是 页面名称
recommendContentType RecommendContentTypeEnum 是 推荐内容类型,见 RecommendContentTypeEnum
recommendTraceId String 是 推荐引擎 trace ID
recommendId String 是 可视窗口内的内容 ID,多个用英文逗号分隔
recommendTraceInfo String 否 推荐引擎 trace info,未接推荐引擎传 '',默认 ''
clientVersion String 否 客户端版本号,如 '1.2.3',默认 ''

6.11 推荐列表点击事件【客户端】

AnalyticsSdk.instance.track(RecommendListClickEvent(
  pageKey: 'home',
  pageName: '首页',
  recommendContentType: RecommendContentTypeEnum.video,
  recommendTraceId: 'rec_trace_abc123',
  recommendId: 'vid_001',
  recommendTraceInfo: '{"source":"server"}',  // 未接推荐引擎传 ''
  clientVersion: '1.2.3',                     // 未接推荐引擎传 ''
));
字段 类型 必填 说明
pageKey String 是 页面标识
pageName String 是 页面名称
recommendContentType RecommendContentTypeEnum 是 推荐内容类型,见 RecommendContentTypeEnum
recommendTraceId String 是 推荐引擎 trace ID
recommendId String 是 被点击内容的 ID
recommendTraceInfo String 否 推荐引擎 trace info,未接推荐引擎传 '',默认 ''
clientVersion String 否 客户端版本号,如 '1.2.3',默认 ''

6.12 强制立即上报

await AnalyticsSdk.instance.flush();

6.13 释放资源

await AnalyticsSdk.instance.dispose();

7. 枚举参考

DeviceEnum

枚举值 label 说明
DeviceEnum.android Android Android 设备
DeviceEnum.iOS iOS iOS 设备
DeviceEnum.pc PC PC 设备

UserTypeEnum

枚举值 label 说明
UserTypeEnum.normal normal 普通用户
UserTypeEnum.vip vip VIP 用户

AuthTypeEnum

枚举值 label 说明
AuthTypeEnum.phone phone 手机号
AuthTypeEnum.deviceid deviceid 设备 ID
AuthTypeEnum.email email 邮箱
AuthTypeEnum.username username 账号

AdTypeEnum

已废弃:adType 字段已改为普通 String,直接传字符串即可,如 'banner'、'feed'、'splash'。无需 import 枚举类。

OrderTypeEnum

枚举值 label 说明
OrderTypeEnum.coinPurchase coin_purchase 金币购买
OrderTypeEnum.vipSubscription vip_subscription VIP 订阅

CurrencyEnum

枚举值 label 说明
CurrencyEnum.cny CNY 人民币
CurrencyEnum.usd USD 美元

PayTypeEnum

枚举值 label 说明
PayTypeEnum.wechat wechat 微信支付
PayTypeEnum.alipay alipay 支付宝
PayTypeEnum.bankCard bank_card 银行卡
PayTypeEnum.applePay apple_pay Apple Pay

ConsumeReasonEnum

枚举值 label 说明
ConsumeReasonEnum.videoUnlock video_unlock 视频解锁
ConsumeReasonEnum.giftSend gift_send 礼物赠送
ConsumeReasonEnum.contentPurchase content_purchase 内容购买

VideoEventEnum

枚举值 key 说明
VideoEventEnum.VIDEO_VIEW video_view 展示
VideoEventEnum.VIDEO_PLAY video_play 播放
VideoEventEnum.VIDEO_PAUSE video_pause 暂停
VideoEventEnum.VIDEO_SHARE video_share 分享
VideoEventEnum.VIDEO_COMPLETE video_complete 播放完成
VideoEventEnum.VIDEO_FORWARD video_forward 快进
VideoEventEnum.VIDEO_REWIND video_rewind 快退

VideoContentTypeEnum

枚举值 label 说明
VideoContentTypeEnum.video video 长视频
VideoContentTypeEnum.shortVideo short_video 短视频

ReadBehaviorEnum

枚举值 key 说明
ReadBehaviorEnum.VIEW view 展示
ReadBehaviorEnum.PAGE_NEXT page_next 下一页
ReadBehaviorEnum.PAGE_PREV page_prev 上一页
ReadBehaviorEnum.COMPLETE complete 读完

ClickItemTypeEnum

枚举值 label 说明
ClickItemTypeEnum.VIDEO video 视频
ClickItemTypeEnum.NOVEL novel 小说
ClickItemTypeEnum.COMIC comic 漫画

RecommendContentTypeEnum

枚举值 label 说明
RecommendContentTypeEnum.video video 视频
RecommendContentTypeEnum.novel novel 小说
RecommendContentTypeEnum.comic comic 漫画

BannerEventEnum

枚举值 label 说明
BannerEventEnum.CLICK click 点击
BannerEventEnum.CLOSE close 关闭
BannerEventEnum.SHOW show 展示

8. SdkConfig 全局参数

在 AnalyticsSdk.instance.init() 之前调用 SdkConfig.configure(...) 覆盖默认值:

SdkConfig.configure(
  uploadInterval: const Duration(seconds: 5),
  autoUploadThreshold: 10,
  maxBatchSize: 200,
  connectionTimeout: const Duration(seconds: 15),
);
参数 默认值 说明
maxQueueSize 5000 内存队列最大容量,超过后新事件丢弃
autoUploadThreshold 10 队列达到此数量时立即触发上报
maxBatchSize 500 单次上报最大事件数
uploadInterval Duration(seconds: 5) 定时上报间隔
clickThrottleDuration Duration(milliseconds: 300) 点击事件节流时间
connectionTimeout Duration(seconds: 10) HTTP 连接超时
readTimeout Duration(seconds: 30) HTTP 读取超时
speedTestTimeout Duration(seconds: 5) 域名测速超时
maxCacheLines 2000 缓存文件最大行数
maxCacheBytes 2 * 1024 * 1024 (2MB) 缓存文件最大字节数
maxSingleEventSize 300 * 1024 (300KB) 单条事件最大字节数,超过丢弃
maxAdImpressionCapacity 10000 广告去重集合最大容量(FIFO 淘汰)

恢复所有默认值:

SdkConfig.reset();

9. 上报数据格式

9.1 公共字段

每个事件上报时,顶层 JSON 自动携带以下公共字段:

字段名 类型 说明
event String 事件类型,如 video_event、ad_impression
channel String 渠道标识
app_id String 应用 ID
uid String 用户 ID
sid String 会话 ID,自动管理
client_ts int 事件创建时间戳(10位,秒)
device String 设备类型(自动采集)
device_id String 设备唯一标识
device_brand String 设备品牌(自动采集)
device_model String 设备型号(自动采集)
user_agent String 客户端标识(自动采集)
system_name String 操作系统名称(自动采集)
system_version String 操作系统版本(自动采集)
device_fingerprint String 设备指纹(自动生成)
fp_version String 指纹版本号
event_id String 事件去重 ID
payload Object 事件业务字段

9.2 上报数据格式

{
  "event": "video_event",
  "channel": "google_play",
  "app_id": "your_app_id",
  "uid": "12345",
  "sid": "sess_abc123",
  "client_ts": 1704067200,
  "device": "Android",
  "device_id": "xxxx",
  "device_brand": "Samsung",
  "device_model": "SM-G991B",
  "user_agent": "...",
  "system_name": "Android",
  "system_version": "14.0",
  "device_fingerprint": "sha256-hex-xxxx",
  "fp_version": "1",
  "event_id": "md5-hash-for-dedup",
  "payload": {
    "event": "video_event",
    "video_id": "vid_001",
    ...
  }
}

9.3 字段校验规则

SDK 在事件入队前会自动校验并修正字段。关键字段不合规时整个事件被静默丢弃;可纠正字段超限时自动截断或补全,不影响上报。

关键字段包括:event、event_id、app_id、sid、client_ts、device(提供了无法识别的值时)、device_id、payload(类型错误时),以及 payload 中的核心业务 ID 字段(video_id / novel_id / comic_id / click_item_id)。

通用类型处理规则

  • 所有字符串字段在校验前均会自动 trim()(去除首尾空格)。
  • 非字符串类型(如 int、bool、null 等)传入字符串字段时视为类型错误,等同于未传该字段(关键字段 → 丢弃事件;可选字段 → 移除该字段)。
  • 数字字段(百分比 / 位置 / 数量 / 时间戳类)支持 int、double 以及纯数字字符串三种类型,均可正常解析。

公共字段

字段 规则 不合规处理
event 小写字母/数字/下划线,最大 64 字符 丢弃事件
event_id 字母/数字,1~32 字符(SDK 自动生成) 丢弃事件
app_id 非空,最大 64 字符 空时丢弃事件;超长截断
channel 可选,最大 128 字符 超长截断
uid 可选,最大 128 字符 超长截断
sid 自动生成;为空时 SDK 自动填充会话 ID 生成失败则丢弃事件
client_ts 整数,范围 946684800 ~ 4102444800(2000 ~ 2100 年) 丢弃事件
device 仅 Android / iOS / PC,大小写自动纠正 丢弃事件
device_id 非空,最大 128 字符 空时丢弃事件;超长截断
device_fingerprint 可选,最大 128 字符 超长截断
fp_version 若指纹非空而版本为空,自动填充默认版本 —

payload 业务字段

类别 代表字段 规则 不合规处理
字段名 所有 key ^[A-Za-z0-9_]+$ 移除该字段
关键 ID video_id / novel_id / comic_id / click_item_id 非空 丢弃事件
普通 ID ad_id / recommend_id 等 非空 移除该字段
逗号分隔 ad_id / novel_tag_key / video_tag_name 等 自动清理各段首尾空格、过滤空段;清洗后仍为空则移除该字段 移除该字段
可选字符串 creative_id / recommend_trace_id 等 可为空 空字符串原样保留
名称字段 page_name / ad_name / video_name 等 最大 128 字符 截断
标题字段 title / content_title / search_keyword 等 最大 256 字符 截断
关键词字段 keywords / tags / description 最大 500 字符 截断
百分比字段 progress / completion_rate / view_ratio 整数,0 ~ 100 移除该字段
位置字段 position / rank / slot / index 整数,≥ 1 移除该字段
数字字段 duration / count / amount 等 整数,≥ 0 移除该字段
时间戳字段 start_ts / end_ts / publish_ts 同 client_ts 范围 移除该字段
其他字段 — 字符串最大 500 字符;非字符串原样保留 截断

9.4 设备指纹(device_fingerprint / fp_version)

设备指纹由 SDK 在 init() 时自动生成,业务方无需手动传入。

采集因子

平台 参与计算的因子
Native(Android / iOS / 桌面) device_id、device(类型)、device_brand、device_model、system_name、system_version
Web device、device_brand、device_model、system_name、system_version、user_agent、屏幕分辨率(WxH)、浏览器语言、时区名称

生成规则

  1. 过滤所有空因子,将剩余因子用 | 拼接后做 SHA-256,取 64 位十六进制字符串作为指纹。
  2. 若所有因子均为空(降级场景),改为生成随机 UUID v4 并持久化至本地(见缓存规则),后续从本地读取保证稳定性。

device_fingerprint 不参与 event_id 的计算。

缓存规则

  • 内存缓存:首次生成后缓存在内存中,同一应用生命周期内直接返回,不重复计算。
  • 持久化缓存(仅降级 UUID 路径):
    • Native:写入 applicationDocumentsDirectory/analytics_fingerprint.txt;
    • Web:写入 localStorage['analytics_fp']。
  • 重新 init() 后若设备因子发生变化,可通过内部 clearCache() 重新计算(一般无需手动调用)。

失败兜底

任何环节发生异常,均静默返回空字符串,事件照常上报;device_fingerprint 为空时,fp_version 字段亦不写入(无指纹则无需版本号)。

fp_version 说明

fp_version 标识当前指纹生成规则的版本(当前为 "1"),供服务端区分不同规则生成的指纹,支持跨版本数据对比分析。生成规则(因子组合或哈希算法)变更时版本号递增。


10. 调试工具

10.1 AnalyticsDebugBanner(悬浮调试条)

在 init() 时传 enableDebugBanner: true,然后将 AnalyticsDebugBanner() 放入 widget 树顶部(通常是页面 Column 的第一个子节点或 Stack 的顶层):

await AnalyticsSdk.instance.init(
  // ...
  enableDebugBanner: true,
);

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            const AnalyticsDebugBanner(),  // 无需任何参数
            Expanded(child: YourContent()),
          ],
        ),
      ),
    );
  }
}

调试条显示 7 个步骤:①域名配置 ②SDK初始化 ③域名列表 ④域名测速 ⑤上报地址 ⑥待上报队列 ⑦最近上报。release 包自动隐藏,无性能影响。

10.2 getDebugSteps / getDebugInfo

final steps = AnalyticsSdk.instance.getDebugSteps();
for (final step in steps) {
  print('${step['name']}: ${step['status']} - ${step['detail']}');
}

final info = AnalyticsSdk.instance.getDebugInfo();
print(info['inited']);
print(info['queueLength']);
print(info['reportUrl']);

10.3 validateEncryptedConfig

final result = AnalyticsSdk.instance.validateEncryptedConfig(encryptedString);
if (result['success'] == true) {
  print('域名数量: ${result['domainCount']}');
  print('事件类型数量: ${result['eventCount']}');
} else {
  print('验证失败: ${result['error']}');
}

仅 debug 包可用,release 包返回错误。

10.4 Logger

import 'package:analytics_sdk/utils/logger.dart';

Logger.enabled = false;  // 关闭所有日志

11. 高级用法

11.1 延迟传入加密配置

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await AnalyticsSdk.instance.init(
    appId: 'your_app_id',
    encryptedConfig: null,     // SDK 尝试读取磁盘缓存域名
    deviceId: await getDeviceId(),
  );

  runApp(const MyApp());

  // 异步获取,不阻塞 UI 启动
  _fetchAndApplyConfig();
}

Future<void> _fetchAndApplyConfig() async {
  try {
    final config = await yourApiClient.fetchEncryptedConfig();
    await AnalyticsSdk.instance.refreshDomainConfig(encryptedConfig: config);
  } catch (_) {}
}

11.2 获取公共参数(getParams)

getParams() 可获取 SDK 当前全部公共参数,适合服务端拼装事件或埋点调试:

// 获取全部参数
final all = AnalyticsSdk.getParams();

// 只取指定字段
final partial = AnalyticsSdk.getParams(['app_id', 'uid', 'device_id', 'device_fingerprint']);

返回 Map<String, dynamic>,包含:app_id、channel、uid、user_type、sid、device、device_id、device_brand、device_model、user_agent、system_name、system_version、device_fingerprint、fp_version。

如果 key 不存在,该 key 不会出现在返回 Map 中(静默忽略)。

11.3 服务端复用设备公共字段

final deviceFields = AnalyticsSdk.getDeviceCommonFields();
// 包含:device, device_id, device_brand, device_model,
//       user_agent, system_name, system_version, device_fingerprint

12. 完整集成示例

import 'package:flutter/material.dart';
import 'package:analytics_sdk/analytics_sdk.dart';
import 'package:analytics_sdk/config/sdk_config.dart';
import 'package:analytics_sdk/enum/user_type_enum.dart';
import 'package:analytics_sdk/manager/page_name_manager.dart';
import 'package:analytics_sdk/widget/global_click_wrapper.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  SdkConfig.configure(
    uploadInterval: const Duration(seconds: 5),
    autoUploadThreshold: 10,
  );

  PageNameMapper.addMappings({
    'home': '首页',
    'video_detail': '视频详情页',
    'user_center': '个人中心',
    'search': '搜索页',
  });

  await AnalyticsSdk.instance.init(
    appId: 'your_app_id',
    encryptedConfig: 'YOUR_BASE64_ENCRYPTED_CONFIG',
    deviceId: 'device_unique_id',
    channel: 'google_play',
    uid: '',
    enableDebugBanner: true,
  );

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GlobalClickWrapper(
      child: MaterialApp(
        navigatorObservers: [
          AnalyticsSdk.instance.pageObserver,
        ],
        initialRoute: '/',
        routes: {
          '/': (context) => const HomePage(),
          '/video_detail': (context) => const VideoDetailPage(),
        },
      ),
    );
  }
}

// ——— 登录场景 ———
void onLoginSuccess(String userId, bool isVip) {
  AnalyticsSdk.setUserIdAndType(
    userId: userId,
    userTypeEnum: isVip ? UserTypeEnum.vip : UserTypeEnum.normal,
  );
}

// ——— 视频播放场景 ———
void onVideoPlay(String videoId, String title) {
  AnalyticsSdk.instance.track(VideoEvent(
    videoId: videoId,
    videoTitle: title,
    videoTypeId: 'action',
    videoTypeName: '动作片',
    videoTagKey: 'hot',
    videoTagName: '热门',
    videoDuration: 7200,
    playDuration: 0,
    playProgress: 0,
    videoBehavior: VideoEventEnum.VIDEO_PLAY,
  ));
}

// ——— 广告展示场景 ———
void onAdsRendered(List<String> adIds) {
  AnalyticsSdk.instance.track(AdImpressionEvent(
    pageKey: 'home',
    pageName: '首页',
    adSlotKey: 'home_feed_ad',
    adSlotName: '首页信息流广告',
    adId: adIds.join(','),
    adType: 'feed',
  ));
}

// ——— 应用退出场景 ———
Future<void> onAppExit() async {
  await AnalyticsSdk.instance.flush();
  await AnalyticsSdk.instance.dispose();
}