本文介绍接入用户体验监控 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)。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)声明: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类子事件会静默失效。要完整验证挣扎检测,需要同时开启行为监控: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 默认关闭,需在初始化时显式开启:
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() 获取实例后调用。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);}
用户标识、设备标识与全局标签:
tdem?.setUser("user-456") // 设置用户标识,用于用户维度聚合tdem?.clearUser() // 清除用户标识tdem?.setDeviceId("device-abc") // 设置设备标识,由宿主提供tdem?.clearDeviceId() // 清除设备标识,回落 not_settdem?.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_setMap<String, Object> tags = new HashMap<>();tags.put("role", "admin");tags.put("team", "dev");tdem.setTags(tags); // 设置 / 追加标签tdem.removeTags(Arrays.asList("team")); // 移除指定标签tdem.clearTags(); // 清空所有标签
隐私标记:自动识别覆盖不到的场景(如订单金额、收货地址这类字面无语义特征的文案),可用
TDEMPrivacyMetadata 主动声明为敏感。import com.tencent.tdem.common.privacy.TDEMPrivacyMetadataimport 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