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

数据上报验证

最近更新时间:2026-09-28 17:02:02
本文档已由 AI 辅助审校
我的收藏
本文介绍接入用户体验监控 Android SDK 后,如何验证各监控能力的数据上报是否成功。

前提条件

崩溃(crashPluginConfig)、ANR(anrPluginConfig)、卡顿(lagPluginConfig)、启动(launchPluginConfig)、网络(httpPluginConfig)、挣扎检测(strugglePluginConfig)、设备信息(devicePluginConfig)默认开启。
用户行为(behaviorPluginConfig)、Session Replay(replayPluginConfig)默认关闭,需显式开启。
验证期间建议设置 .logLevel(TDEM.LEVEL_DEBUG),方便观察上报请求与运行日志。日志输出开关由 SDK 内部固定开启,logLevel 只控制输出级别下限,默认 WARN。

步骤1:检查上报请求

初始化后,SDK 会发出下列请求(Session Replay 默认关闭,开启后才有回放上报)。HTTP 2xx / 204 均视为成功:
端点
内容
POST {url}/api/v1/config
远程配置,仅在返回启用时才挂载各监控插件
POST {url}/api/v1/collect/events
标准事件(崩溃 / ANR / 卡顿 / 启动 / 网络 / 行为 / 挣扎 / 自定义事件等)
POST {url}/api/v1/collect/replay
Session Replay 录屏数据
url 填站点根地址即可,SDK 会自行拼接路径,无需手动补 /api/v1。

步骤2:验证各监控能力

崩溃监控

初始化后,可在页面中主动触发 Java 异常来验证:
// 触发未捕获的 Java 异常
throw RuntimeException("tdem verify crash")
Native 崩溃(SIGSEGV / SIGABRT)通过 Signal Handler 捕获,可在 demo 中触发验证。

ANR 监控

主线程 sleep 或死循环可触发 SIGQUIT ANR 检测:
Thread.sleep(10_000) // 主线程阻塞触发 ANR

卡顿监控

主线程执行长时间任务,超过 lagThresholdMs(默认1000ms)即可触发卡顿上报。

启动监控

启动监控默认开启,通过 ASM 编译期插桩实现,早于 SDK 初始化执行,不依赖 TDEM.init 的调用时机。
启动类型共6种:
first_start:首次安装。
upgrade_start:版本升级后首次启动。
cold_start:进程被杀后重新启动。
warm_start:从后台切回且停留超过阈值。
preheat_start:从后台切回且停留不超过阈值,即热启动。
prepare_start:进程由 Service / BroadcastReceiver / ContentProvider 触发。
后台停留阈值由 warmStartThresholdMs 控制,默认 180000ms。
默认在首个 Activity resume 后自动封口并上报事件 app_launch。若配置 .launchPluginConfig(LaunchPluginConfig(manualEndEnabled = true)),则改为等待业务调用 TDEM.endLaunch() 后才封口。
首次安装后的第一次启动不采集:启动监控的开关通过 SharedPreferences 持久化,第一次启动时开关尚未写入,SDK 初始化成功写入后,第二次及后续启动才正式开启。验证启动数据需要至少冷启动两次。
验证方式:冷启动 App 后观察 Logcat 中的启动日志。

网络监控

网络监控默认开启,SDK 自动监听 OkHttp3 / HttpURLConnection / SSE 请求并上报耗时与错误。可通过配置 slowThresholdMs 验证慢请求上报。

行为监控

开启行为监控后,SDK 自动采集以下行为事件:页面(page_view)、触摸(click / double_tap / long_press / scroll / pinch_zoom)、按键与系统(key_press / screen_toggle / screen_rotation)、输入与表单(text_input / form_focus / form_blur / form_change)。
Kotlin
Java
val config = TDEMConfiguration.Builder(this)
.id("your-project-key")
.url("https://dem.rumt-zh.com")
.behaviorPluginConfig(BehaviorPluginConfig(enabled = true))
.build()
TDEM.init(config)
import android.app.Application;
import com.tencent.tdem.TDEM;
import com.tencent.tdem.TDEMConfiguration;
import com.tencent.tdem.behavior.BehaviorPluginConfig;

TDEMConfiguration config = new TDEMConfiguration.Builder(this)
.id("your-project-key")
.url("https://dem.rumt-zh.com")
.behaviorPluginConfig(new BehaviorPluginConfig(true))
.build();
TDEM.init(config);
手动逻辑页可通过 startPage / leavePage(LIFO)声明:
Kotlin
Java
TDEM.getInstance()?.startPage("checkout")
// ... 页面逻辑 ...
TDEM.getInstance()?.leavePage()
import com.tencent.tdem.TDEM;

TDEM.getInstance().startPage("checkout");
// ... 页面逻辑 ...
TDEM.getInstance().leavePage();

挣扎检测

挣扎检测默认开启,产出5类子事件:
rage_click:同一元素1秒内连续点击5次。
dead_click:点击后3秒内既无页面跳转也无接口调用,判定为无响应。
error_click:点击后3秒内出现接口错误或 JS / Promise 错误。
long_focus_time:表单控件持续聚焦超过20秒。
back_forward:10秒内连续返回3次。
规则阈值内部写死,接入方不可修改。可通过 clickRulesEnabled / formRulesEnabled / navigationRulesEnabled 分别关闭点击类、表单类与返回类规则。
需要注意的是,默认配置下通常只有 back_forward 能被触发。rage_click / dead_click / error_click 依赖点击事件,long_focus_time 依赖表单聚焦事件,而这些事件全部由用户行为监控(behaviorPluginConfig)产生,行为监控默认关闭,因此这4类子事件会静默失效。要完整验证挣扎检测,需要同时开启行为监控:
Kotlin
Java
val config = TDEMConfiguration.Builder(this)
.id("your-project-key")
.url("https://dem.rumt-zh.com")
.behaviorPluginConfig(BehaviorPluginConfig(enabled = true)) // 必需:提供点击 / 表单事件源
.strugglePluginConfig(StrugglePluginConfig(enabled = true))
.build()
TDEM.init(config)
import android.app.Application;
import com.tencent.tdem.TDEM;
import com.tencent.tdem.TDEMConfiguration;
import com.tencent.tdem.behavior.BehaviorPluginConfig;
import com.tencent.tdem.struggle.StrugglePluginConfig;

TDEMConfiguration config = new TDEMConfiguration.Builder(this)
.id("your-project-key")
.url("https://dem.rumt-zh.com")
.behaviorPluginConfig(new BehaviorPluginConfig(true)) // 必需:提供点击 / 表单事件源
.strugglePluginConfig(new StrugglePluginConfig())
.build();
TDEM.init(config);
back_forward 消费的是页面事件,而页面事件有不受行为监控开关影响的来源(startPage / setPage / leavePage),因此它单独可用。
验证方式:连点同一按钮5次以上触发 rage_click,或在10秒内连续返回3次触发 back_forward,随后观察 Logcat 中的挣扎日志。

会话回放

Session Replay 默认关闭,需在初始化时显式开启:
Kotlin
Java
val config = TDEMConfiguration.Builder(this)
.id("your-project-key")
.url("https://dem.rumt-zh.com")
.replayPluginConfig(ReplayConfig(enabled = true))
.build()
TDEM.init(config)
import android.app.Application;
import com.tencent.tdem.TDEM;
import com.tencent.tdem.TDEMConfiguration;
import com.tencent.tdem.replay.ReplayConfig;
import com.tencent.tdem.replay.ReplayQuality;
import com.tencent.tdem.replay.ScreenshotStrategyType;
import java.util.Collections;

TDEMConfiguration config = new TDEMConfiguration.Builder(this)
.id("your-project-key")
.url("https://dem.rumt-zh.com")
.replayPluginConfig(new ReplayConfig(
true, 0.1, 1.0, ReplayQuality.LOW, false, false,
ScreenshotStrategyType.PIXEL_COPY,
ReplayConfig.Companion.getDEFAULT_MASK_VIEW_CLASSES(),
Collections.<String>emptySet()))
.build();
TDEM.init(config);
采样是双通道:sessionSampleRate(默认0.1)决定常规会话是否被录制;未命中时若 onErrorSampleRate(默认1.0)大于0,SDK 会走滚动缓冲策略,在发生错误时冲刷并上报。
控制台下发的远程配置 replay_sample_rate(0 - 100)会覆盖本地 sessionSampleRate。因此“开启了回放却没看到数据”不一定是接入失败,需要依次确认:本地 enabled 已开启、控制台远程配置已启用回放、且会话被采样命中。
验证方式:开启后操作 App 若干秒,观察 Logcat 中的会话回放日志。

设备信息

设备信息采集默认开启,SDK 会在每条事件的上下文中附带下列设备字段:os / device_type / device_model / app_version / sdk_version / network_type / screen_resolution / language / carrier / storage_free_mb / memory_free_mb
验证方式:在控制台打开任一事件的详情,确认事件上下文中的上述字段已填充。

步骤3:主动上报(可选)

除自动采集外,SDK 提供下列主动上报接口,均通过 TDEM.getInstance() 获取实例后调用。
Kotlin
Java
val tdem = TDEM.getInstance()

// 自定义事件
tdem?.track(
"order_submit",
tags = mapOf("channel" to "app"),
properties = mapOf("amount" to 99),
)

// 自定义测速:可直接传耗时,或 start / end 成对使用
tdem?.measure("api_latency", 320, tags = mapOf("api" to "/user/info"))
tdem?.startMeasure("render")
val duration = tdem?.endMeasure("render") // 返回耗时(ms);未调用 startMeasure 时返回 -1

// 日志与异常:用于上报业务已捕获、未导致崩溃的异常
tdem?.captureMessage("用户完成注册", level = "info")
try {
// 业务代码
} catch (e: Exception) {
tdem?.captureException(e, tags = mapOf("module" to "payment"))
}
import com.tencent.tdem.TDEM;
import java.util.HashMap;
import java.util.Map;

TDEM tdem = TDEM.getInstance();

// 自定义事件
Map<String, String> tags = new HashMap<>();
tags.put("channel", "app");
Map<String, Object> properties = new HashMap<>();
properties.put("amount", 99);
tdem.track("order_submit", tags, properties);

// 自定义测速:可直接传耗时,或 start / end 成对使用
Map<String, String> measureTags = new HashMap<>();
measureTags.put("api", "/user/info");
tdem.measure("api_latency", 320, measureTags, new HashMap<String, Object>());
tdem.startMeasure("render");
long duration = tdem.endMeasure("render"); // 返回耗时(ms);未调用 startMeasure 时返回 -1

// 日志与异常:用于上报业务已捕获、未导致崩溃的异常
tdem.captureMessage("用户完成注册", "info", null);
try {
// 业务代码
} catch (Exception e) {
Map<String, Object> crashTags = new HashMap<>();
crashTags.put("module", "payment");
tdem.captureException(e, crashTags);
}
用户标识、设备标识与全局标签:
Kotlin
Java
tdem?.setUser("user-456") // 设置用户标识,用于用户维度聚合
tdem?.clearUser() // 清除用户标识
tdem?.setDeviceId("device-abc") // 设置设备标识,由宿主提供
tdem?.clearDeviceId() // 清除设备标识,回落 not_set
tdem?.setTags(mapOf("role" to "admin", "team" to "dev")) // 设置 / 追加标签
tdem?.removeTags(listOf("team")) // 移除指定标签
tdem?.clearTags() // 清空所有标签
import com.tencent.tdem.TDEM;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

TDEM tdem = TDEM.getInstance();

tdem.setUser("user-456"); // 设置用户标识,用于用户维度聚合
tdem.clearUser(); // 清除用户标识
tdem.setDeviceId("device-abc"); // 设置设备标识,由宿主提供
tdem.clearDeviceId(); // 清除设备标识,回落 not_set

Map<String, Object> tags = new HashMap<>();
tags.put("role", "admin");
tags.put("team", "dev");
tdem.setTags(tags); // 设置 / 追加标签
tdem.removeTags(Arrays.asList("team")); // 移除指定标签
tdem.clearTags(); // 清空所有标签
验证方式:调用后在控制台对应的自定义事件、日志或用户页确认数据已入库。完整签名与参数说明见 API 说明。
隐私标记:自动识别覆盖不到的场景(如订单金额、收货地址这类字面无语义特征的文案),可用 TDEMPrivacyMetadata 主动声明为敏感。
Kotlin
Java
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.INHERIT) // 恢复默认判定
import android.view.View;
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.INHERIT); // 恢复默认判定
标记父容器时,Session Replay 会把标记向下传递(等价于遮罩整棵子树),但行为监控只判定被标记的那个控件本身,子控件文本仍会被采集,需逐个标记。
验证方式:开启 Session Replay 后操作 App,确认被标记的控件在录像中已被遮罩,且该控件的文本不再出现在行为事件中。TDEMReplayMasking 取值与完整遮罩判定次序见 API 说明中的 隐私标记。

步骤4:检查数据上报

在 Logcat 中观察 SDK 日志。全模块日志统一使用 TDEM-Android 作为 tag,模块名只出现在日志正文中:
adb logcat -s TDEM-Android:D

# 只看某个模块(如网络)的日志
adb logcat -s TDEM-Android:D | grep HttpPlugin