4. 手动埋点基础:点击事件追踪

手动埋点是 Compose 组件中最基础且最灵活的事件追踪方式。开发者需要在组件的 onClick 回调中显式插入埋点上报逻辑,精确控制哪些点击行为需要记录、携带哪些参数。本章从原理出发,逐步讲解点击埋点的完整实现流程。

4.1 核心原理

手动点击埋点的本质是在用户触发交互事件的回调函数中,调用埋点 SDK 的上报接口。其数据流遵循“事件捕获 → 数据组装 → 异步发送”三段模型:

  • 事件捕获:Compose 通过 Modifier.clickable 或 onClick lambda 拦截点击事件。
  • 数据组装:在回调中创建埋点事件对象,填充 event_name、page_id、timestamp、ext_params 等字段。
  • 异步发送:将事件对象交给埋点 SDK 的队列处理,避免阻塞 UI 线程。
关键区分:手动埋点与自动埋点(AOP/AspectJ)的区别在于埋点代码由开发者逐点编写,可读性和可控性高,但维护成本随组件数量线性增长。适用于核心路径或需要精细参数的场景。

4.2 实现流程

以下步骤展示在 Compose 中为 Button 和 Image 绑定点击埋点的完整流程。

  1. 引入埋点 SDK:在 build.gradle 中添加埋点库依赖,例如 com.example:tracking-sdk:1.2.0。
  2. 初始化埋点 SDK:在 Application.onCreate() 中初始化,并获取单例或注入上报接口。
  3. 定义埋点事件模型:创建数据类 ClickEvent 包含必要字段。
  4. 编写点击回调:在 Button 或 Image 的 onClick 中调用上报方法。
  5. 传递页面上下文:将当前页面名称/ID 通过参数或 LocalContext 传入。
  6. 验证上报:使用 Logcat 或调试工具观察发送日志。

4.3 关键代码实现

事件模型定义:

data class ClickEvent(
    val eventName: String,
    val pageId: String,
    val componentId: String?,
    val timestamp: Long = System.currentTimeMillis(),
    val ext: Map<String, String> = emptyMap()
)

埋点上报工具类:

object Tracker {
    private val eventQueue = mutableListOf<ClickEvent>()
    fun track(event: ClickEvent) {
        eventQueue.add(event)
        // 异步发送:实际项目中通过线程池或 RxJava 分发
        sendEvent(event)
    }
    private fun sendEvent(event: ClickEvent) {
        android.util.Log.d("Tracker", "Sending event: $event")
        // 此处调用 SDK 上报接口
    }
}

Button 绑定埋点:

@Composable
fun TrackedButton(pageId: String) {
    Button(
        onClick = {
            Tracker.track(
                ClickEvent(
                    eventName = "btn_submit_click",
                    pageId = pageId,
                    componentId = "submit_button"
                )
            )
            // 业务逻辑
        }
    ) {
        Text("提交")
    }
}

Image 绑定埋点:

@Composable
fun TrackedImage(
    painter: Painter,
    contentDescription: String?,
    pageId: String,
    imgId: String,
    onClick: () -> Unit
) {
    Image(
        painter = painter,
        contentDescription = contentDescription,
        modifier = Modifier.clickable {
            Tracker.track(
                ClickEvent(
                    eventName = "img_ad_click",
                    pageId = pageId,
                    componentId = imgId,
                    ext = mapOf("position" to "banner")
                )
            )
            onClick()
        }
    )
}

4.4 调试与验证

在开发阶段通过以下方式确认埋点是否生效:

  • Logcat 过滤:使用 adb logcat -s Tracker 查看上报日志。
  • 网络拦截:使用 Charles 或 HTTP 代理查看 SDK 发送的请求包。
  • 断点调试:在 sendEvent 方法内下断点,检查 ClickEvent 字段值。
常见问题:若埋点未触发,检查 onClick 是否被短路(如 enabled = false),或 Modifier.clickable 与其他手势处理冲突导致事件未传递。

4.5 部署注意事项

环节要点
混淆配置在 proguard-rules.pro 中添加 -keep class com.example.tracking.** { *; }
多模块传递使用 CompositionLocal 绑定页面 ID,避免硬编码。
批量发送采用定时或数量阈值缓冲区,减少网络请求次数。

4.6 性能与稳定性优化

  • 避免主线程阻塞:上报操作必须切换至 IO 线程,可使用 Dispatchers.IO 包裹。
  • 事件去重:对高频点击(如连点)采样或去重,防止服务端压力。
  • 异常隔离:使用 try-catch 捕获上报异常,不因埋点崩溃影响业务。
最佳实践:将埋点抽象为单独的 TrackedButton 可组合函数,接收 eventName 和 pageId 参数,减少重复代码。同时利用 Compose 的 derivedStateOf 或 remember 避免不必要的重组。
手动点击埋点数据流 用户点击 Button / Image onClick 回调 构建 ClickEvent Tracker.track() 埋点 SDK 发送 全部代码在 onClick 中 显式调用 track()

上图展示了用户点击 Button 或 Image 后,经过 onClick 回调进入埋点构建阶段,最终由 Tracker.track() 异步发送至 SDK 的完整流程。所有埋点代码书写位置均在绿色框与橙色框之间的回调函数内,属于最典型的手动埋点模式。