帮你快速理解、总结文档立即下载

API 说明

最近更新时间:2026-09-28 17:02:02
本文档已由 AI 辅助审校
我的收藏
本文详细介绍用户体验监控 Android SDK 的各功能接口,帮助您更灵活、深度地使用 SDK。

初始化

通过 TDEM.init(config) 创建实例并自动开始采集,重复调用会返回同一实例;初始化后通过 TDEM.getInstance() 获取单例调用实例 API,未初始化时返回 null。
TDEM 上的静态方法共5个:
方法
用途
TDEM.init(config)
创建并启动 SDK,返回实例
TDEM.getInstance()
获取单例,未初始化返回 null
TDEM.addInitListener(listener)
注册启动结算监听,可在 init 之前调用
TDEM.removeInitListener(listener)
注销启动结算监听
TDEM.endLaunch()
业务就绪时手动封口启动测量
另有日志级别常量 TDEM.LEVEL_DEBUG / TDEM.LEVEL_INFO / TDEM.LEVEL_WARN / TDEM.LEVEL_ERROR 与版本常量 TDEM.VERSION。
Kotlin
Java
val tdem = TDEM.getInstance()
TDEM tdem = TDEM.getInstance(); // 未初始化时返回 null
启动结算可异步感知。TDEM.addInitListener 既可在 TDEM.init 之前注册,也可在之后注册;远程配置闸门落定后回调一次,若注册时决策已落定则立即回放结果。
Kotlin
Java
import com.tencent.tdem.core.TDEMInitListener

val listener = object : TDEMInitListener {
override fun onInitSettled(started: Boolean, remoteConfig: RemoteConfigResult?) {
// started:是否已进入采集态
// remoteConfig:本次决策使用的远程配置;拉取耗尽且无缓存时为 null
}
}

TDEM.addInitListener(listener) // 可在 TDEM.init 之前调用
TDEM.removeInitListener(listener) // 注销
import com.tencent.tdem.core.TDEMInitListener;

TDEMInitListener listener = (started, remoteConfig) -> {
// started:是否已进入采集态
// remoteConfig:本次决策使用的远程配置;拉取耗尽且无缓存时为 null
};

TDEM.addInitListener(listener);
TDEM.removeInitListener(listener);
回调覆盖三种结果:成功启动、远端关闭或未抽中、拉取耗尽;若 id / url 为空导致初始化被跳过,同样会以 started=false 回调。实例层另有 registerInitListener / unregisterInitListener,语义相同;接入方应统一使用上述入口层静态方法,避免 JVM 签名冲突。

自定义事件

Kotlin
Java
tdem?.track(
"button_click",
tags = mapOf("button_id" to "submit"),
properties = mapOf("page" to "/checkout"),
)
import java.util.HashMap;
import java.util.Map;

Map<String, String> tags = new HashMap<>();
tags.put("button_id", "submit");
Map<String, Object> properties = new HashMap<>();
properties.put("page", "/checkout");

tdem.track("button_click", tags, properties);

自定义测速

Kotlin
Java
// 直接上报耗时(毫秒)
tdem?.measure("api_latency", 320, tags = mapOf("api" to "/user/info"))

// 或使用计时器
tdem?.startMeasure("render")
// ... 执行操作 ...
val duration = tdem?.endMeasure("render") // 返回耗时(ms),未找到 start 返回 -1
// 直接上报耗时(毫秒)
Map<String, String> tags = new HashMap<>();
tags.put("api", "/user/info");
tdem.measure("api_latency", 320, tags, new HashMap<String, Object>());

// 或使用计时器
tdem.startMeasure("render");
// ... 执行操作 ...
long duration = tdem.endMeasure("render"); // 返回耗时(ms),未找到 start 返回 -1
measure / endMeasure 支持省略末尾可选参数的短重载,因此 Java 侧也可写成 tdem.measure("api_latency", 320) 或 tdem.endMeasure("render")。

日志与异常上报

Kotlin
Java
tdem?.captureMessage("用户完成注册", level = "info", tags = mapOf("step" to "register"))

try {
riskyOperation()
} catch (e: Exception) {
tdem?.captureException(e, tags = mapOf("module" to "payment"))
}
Map<String, Object> tags = new HashMap<>();
tags.put("step", "register");
tdem.captureMessage("用户完成注册", "info", tags);

Map<String, Object> crashTags = new HashMap<>();
crashTags.put("module", "payment");
try {
riskyOperation();
} catch (Exception e) {
tdem.captureException(e, crashTags);
}
captureMessage / captureException 未提供短重载,Java 侧需显式传出全部参数(level 传 null 等同于不写该字段)。

用户标识管理

tdem?.setUser("user-456") // 设置用户
tdem?.clearUser() // 清除用户

隐私标记

通过 TDEMPrivacyMetadata 主动声明某个控件为敏感,用于自动识别覆盖不到的场景(例如订单金额、收货地址这类字符串本身无语义特征的文案)。
import com.tencent.tdem.common.privacy.TDEMPrivacyMetadata
import com.tencent.tdem.common.privacy.TDEMReplayMasking

// 标记为敏感:行为监控不采集该控件文本,Session Replay 遮罩该控件及其子树
TDEMPrivacyMetadata.setSensitive(orderAmountText, true)
TDEMPrivacyMetadata.setSensitive(orderAmountText, false) // 取消标记

// 只调 Session Replay 遮罩,不影响文本采集
TDEMPrivacyMetadata.setReplayMasking(qrCodeView, TDEMReplayMasking.MASKED)
TDEMPrivacyMetadata.setReplayMasking(qrCodeView, TDEMReplayMasking.UNMASKED)
TDEMPrivacyMetadata.setReplayMasking(qrCodeView, TDEMReplayMasking.INHERIT) // 恢复默认判定
TDEMReplayMasking 三个取值:INHERIT(默认,按下面的遮罩判定链走)、MASKED(强制遮罩)、UNMASKED(显式不遮罩)。
两条通道作用范围与继承语义不同:
通道
影响
对子控件是否继承
setSensitive
行为监控文本采集 + Session Replay 遮罩
行为监控不继承,Session Replay 继承
setReplayMasking
仅 Session Replay 遮罩
不继承
标记父容器时:Session Replay 会把标记向下传递,等价于遮罩整棵子树;但行为监控只判定被标记的那个控件本身,子控件文本仍会被采集,需逐个标记。
Session Replay 的遮罩判定按下列次序,命中即返回:
1. 业务敏感标记(setSensitive,含父容器继承):最高优先级,不可被任何标记撤销。
2. sentry-unmask Tag → 不遮罩
3. sentry-mask Tag → 遮罩
4. setReplayMasking(MASKED) → 遮罩
5. setReplayMasking(UNMASKED) → 不遮罩(次序在显式遮罩之后,但仍受第 1 条压制)。
6. unmaskViewClasses 匹配 → 不遮罩
7. maskViewClasses 匹配 → 遮罩
因此对已标记敏感的控件调用 setReplayMasking(UNMASKED) 不会放开遮罩。TDEMPrivacyMetadata.isSensitive(view) / replayMasking(view) 可查询当前取值,后者未设置时返回 INHERIT。

行为元数据

BehaviorViewMetadata 用于在自动识别拿不到稳定语义时,为单个控件补充行为侧元数据。点击或长按事件仍会正常上报,这些标记只影响事件属性与下游判定。
Kotlin
Java
import com.tencent.tdem.behavior.BehaviorViewMetadata

// 该控件的点击仍会上报,但不计入「无响应点击」判定
BehaviorViewMetadata.setDeadClickIgnored(buyButton, true)
BehaviorViewMetadata.isDeadClickIgnored(buyButton) // 查询,未设置返回 false

// 把行为事件与业务反馈键关联
BehaviorViewMetadata.setStruggleFeedbackKey(buyButton, "coupon_refresh")
BehaviorViewMetadata.struggleFeedbackKey(buyButton) // 查询,未设置返回 null
import com.tencent.tdem.behavior.BehaviorViewMetadata;

BehaviorViewMetadata.setDeadClickIgnored(buyButton, true);
BehaviorViewMetadata.isDeadClickIgnored(buyButton);

BehaviorViewMetadata.setStruggleFeedbackKey(buyButton, "coupon_refresh");
BehaviorViewMetadata.struggleFeedbackKey(buyButton);
两个标记各自写入行为事件的 event_properties:
方法
写入的事件属性
说明
setDeadClickIgnored(view, true)
dead_click_ignored=1
该控件的点击不计入「无响应点击」判定;传 false 撤销
setStruggleFeedbackKey(view, key)
struggle_feedback_key
把行为事件与业务反馈键关联;key 为空白或 null 时撤销
标记挂在 View 实例上,内部使用弱引用,控件被回收后自动释放,不需要手动清理。
注意:
页面 ID 与控件 ID 由 SDK 自动解析(优先取 resource-id,取不到时回落控件层级路径),Android 没有手动指定 page ID / element ID 的接口。若只是想在检测阶段跳过一批控件、不写事件属性,应改用 StrugglePluginConfig 的 deadClickIgnoredViewIds / ignoreDeadClickSelectors 配置项。

设备标识管理

tdem?.setDeviceId("device-abc") // 设置设备标识,由宿主提供
tdem?.clearDeviceId() // 清除设备标识,回落 not_set
设备标识由业务侧提供,SDK 不会自行读取 ANDROID_ID 等系统标识;未设置或传空白时上报字面量 not_set。也可在 TDEMConfiguration 的 deviceId 中于初始化时一并设置。

全局标签管理

tdem?.setTags(mapOf("role" to "admin", "team" to "dev")) // 设置/追加标签
tdem?.removeTags(listOf("team")) // 移除指定标签
tdem?.clearTags() // 清空所有标签

页面管理

// 手动逻辑页(LIFO,覆盖自动 Activity/Fragment/Compose 页)
tdem?.startPage("checkout")
tdem?.leavePage() // LIFO pop,栈空时幂等 no-op

// 替换当前逻辑页并发 page_view(navigation_type=replace,RN SPA 切页用)
tdem?.setPage("/modal/confirm", pageTitle = "确认弹窗")

上下文与状态查询

除写入接口外,SDK 也提供读取当前运行状态的接口,便于接入自检与问题定位。
Kotlin
Java
tdem?.getTags() // 当前全局标签快照,Map<String, Any>
tdem?.currentPageUrl // 当前页面标识
tdem?.getCurrentSessionId() // 当前会话 ID,会话未就绪返回 ""
tdem?.getCurrentReplayId() // 当前回放 ID,未启用回放返回 ""
tdem?.userId // 当前用户标识,未设置返回 null
tdem?.deviceId // 当前设备标识,未设置返回 not_set
tdem?.initialized // SDK 是否已进入采集态
import java.util.Map;

Map<String, Object> tags = tdem.getTags();
String pageUrl = tdem.getCurrentPageUrl();
String sessionId = tdem.getCurrentSessionId();
String replayId = tdem.getCurrentReplayId();
String userId = tdem.getUserId();
String deviceId = tdem.getDeviceId();
getCurrentSessionId() 在会话尚未就绪时返回空串;
getCurrentReplayId() 在未启用回放时返回空串;
currentPageUrl 的取值为「手动页面栈顶 > 自动识别的 Activity / Fragment / Compose 页 > unknown」。
resolveReplayLink(timestampMs) 按事件时间戳反查所属回放段,返回 replayId 与相对段起点的 offsetMs,无命中窗口时返回 null:
Kotlin
Java
val link = tdem?.resolveReplayLink(eventTimeMs)
if (link != null) {
println("replay=${link.replayId}, offset=${link.offsetMs}ms")
}
import com.tencent.tdem.core.replay.ReplayAssociationStore;

ReplayAssociationStore.Link link = tdem.resolveReplayLink(eventTimeMs);
if (link != null) {
System.out.println("replay=" + link.getReplayId() + ", offset=" + link.getOffsetMs() + "ms");
}
以下两个接口属进阶用法,一般由插件或桥接层使用:
isPluginEnabled(pluginEnabled, remoteEnabled) 做插件挂载的两级决策(远程配置明确关闭时返回 false,否则取本地配置)。
putContextField(key, value) 向 Protocol v2 批量上下文追加自定义字段(key 空白或 value 为空时忽略)。

挣扎事件上报

// 业务主动上报自定义挣扎事件(无需理解或填写 owner)
tdem?.trackStruggle(
"payment_declined",
severity = TDEMStruggleSeverity.HIGH, // LOW(1) / MEDIUM(3) / HIGH(5),默认 MEDIUM
properties = mapOf("reason" to "risk_rejected", "retry_count" to 2),
tags = mapOf("channel" to "checkout"),
)

原始事件上报

// 直接上报 TDEMEvent 格式的事件(高级用法,SDK 会自动补齐 page_url、session_id 与 tags)
tdem?.reportTDEMEvent(
TDEMEvent(
eventType = EventType.CUSTOM,
eventCategory = EventCategory.CUSTOM,
pageUrl = currentPageUrl,
sessionId = tdem.getCurrentSessionId(),
data = JSONObject().put("event_name", "my_event"),
)
)

启动封口

业务就绪时结束启动测量。需先配置 LaunchPluginConfig(manualEndEnabled = true):
TDEM.endLaunch()

配置修改与销毁

tdem?.setUser("new-user") // 动态修改用户标识
tdem?.destroy() // 销毁实例

版本与日志

通过 TDEM.VERSION 可获取 SDK 版本号字符串。日志由全局单例 TDEMLogger 控制:
Kotlin
Java
import com.tencent.tdem.common.log.TDEMLogger

TDEMLogger.enabled = true // 全局日志开关,修改时同步下推 native
TDEMLogger.tag = "TDEM-Android" // Logcat tag,默认 TDEM-Android
TDEMLogger.level = TDEM.LEVEL_DEBUG // 级别:DEBUG / INFO / WARN / ERROR,默认 DEBUG
import com.tencent.tdem.common.log.TDEMLogger;

TDEMLogger.INSTANCE.setEnabled(true);
TDEMLogger.INSTANCE.setTag("TDEM-Android");
TDEMLogger.INSTANCE.setLevel(TDEM.LEVEL_DEBUG);
TDEMLogger 是 Kotlin object,Java 侧通过 TDEMLogger.INSTANCE 访问,属性读写为 getEnabled() / setEnabled(...)、getTag() / setTag(...)、getLevel() / setLevel(...)(enabled 为普通声明式属性,Java 侧不是 isEnabled())。日志级别常量 TDEM.LEVEL_* 也可直接用于 TDEMConfiguration.Builder.logLevel(...)。SDK 初始化时会按配置同步一次日志开关与级别,通常无需手动设置。

环境枚举

用于标识应用部署所属环境,区分不同环境的数据上报、日志采集与监控策略,枚举定义如下:
production:生产环境。
development:开发环境。
gray:灰度环境。
pre:预发布环境。
daily:日发布环境。
local:本地环境。
test:测试环境。
others:其他环境(未知值归一于此)。