利用Live Templates打造埋点自动化利器

背景

埋点是我们大力智能技术团队的日常开发中比较重要,但又是比较繁琐的一环。每次到埋点的时候,都有一个老码农在勤勤恳恳搬砖的既视感。嗯,这个按键是ctrl,这个按键是c,这个是按键ctrl,嗯,再来一个v。欧耶,成功了,召唤神龙!!!然而神龙并没有出现。what the f**k ,都已经2021年了为啥我还要一个一个把埋点事件Event的名字从文档搬到工程里?还得取个一模一样但是全部大写的变量名。还有那个Key和Value。每次我埋点的时候,你就不能主动一下告诉我,你是属于这个Event的吗?这个年代还不主动,你们是想单身一辈子吗?为了维护一个老码农的尊严。我决定和他们死磕到底。

最终效果

先上代码:

一键导入Event,Key,Value

导入前

public interface Track {
    String KEY_FROM_PAGE = "key_from_page";
    String PAGE_STAY = "page_stay";
    String PAGE_ENTER = "page_enter";
    
    interface Page {
    }
    
    interface Event {
    }
    
    interface Enter {
        String PROFILE_DETAIL_ENTER = "profile_detail_enter";
    }
    
    interface KEY {
    }
    
    interface Value {
    }
}
复制代码

导入后

public interface Track {
    String KEY_FROM_PAGE = "key_from_page";
    String PAGE_STAY = "page_stay";
    String PAGE_ENTER = "page_enter";
    
    interface Page {
        String POINT_READING_RECORD = "point_reading_record";
    }
    
    interface Event {
        String POINT_READING_RECORD_BOOK_ENTER = "point_reading_record_book_enter";
        String POINT_READING_SETTING = "point_reading_setting";
        String BIND_PUSH_REQUIRE = "bind_push_require";
        String HARDWARE_BIND_FINISH = "hardware_bind_finish";
        String POINT_READING_CHECK_RESULT = "point_reading_check_result";
        String POINT_READING_CHECK_AGAIN = "point_reading_check_again";
        String POINT_READING_GUIDE_CLICK = "point_reading_guide_click";
        String POINT_READING_SUPPORT_BOOK_ENTER = "point_reading_support_book_enter";
        String POINT_READING_DETAIL_ENTER = "point_reading_detail_enter";
        String POINT_READING_FEEDBACK = "point_reading_feedback";
        String POINT_READING_RECORD_ENTER = "point_reading_record_enter";
    }
}
复制代码

一键生成埋点方法

生成前

class TapReadTrackCenter(val handler: ITrackHandler) {

}
复制代码

生成后

class TapReadTrackCEnter(val handler: ITrackHandler) {
    fun logPOintReadingScaleEvent() {
        //Track.Event.POINT_READING_SCALE.log (handler, 
        //Track.Key.STATUS to Track.Value ,
        //Track.Key.ACTION to Track.Value 
        //)
    }
    fun logPointReadingSettingEvent() { 
        //Track. Event. POINT_READING_SETTING.log (handler, 
        //Track.Key.PAGE_ID to Track.Value ,
        //Track.Key.IMAGE_URI to Track.Value ,
        //Track.Key.B00K_ID to Track.Value ,
        //Track.Key.PAGE_NO to Track.Value T ,
        //Track.Key.RESULT to Track.Value 
        //)
    }
    fun logPointReadingFeedbackEvent() {
        //Track. Event. POINT_READING_FEEDBACK.log(handler, 
        //Track.Key.PAGE_ID to Track.Value ,
        //Track.Key.IMAGE_URI to Track.Value ,
        //Track.Key.BOOK_ID to Track.Value ,
        //Track.Key.PAGE_NO to Track.Value ,
        //Track.Key.CONTENT_to Track.Value
        //)
    }
}
复制代码

我用人格发誓:上面的代码绝对不是我手打上去的。

实现思路

Live Templates

Live Templates是Android Studio提供给我们的一个很便利的工具。但是长期以来,它的使用场景仅限于:

  1. 输入psfi,生成public static final int
  2. 输入logd,生成HLogger.tag(TAGTAG).d({“fileNamefileName classNameclassName functionNamefunctionName InfoInfo“})

这让我想起了健身教练曾经说过的话:为什么中下斜方肌没有自体重训练动作?因为这两块肌肉力量太强大了,你用自体重训练你就是在侮辱他们。我深刻的感觉到Live Templates长期起来也受到了严重的侮辱。em…

Groovy

其实Live Templates是支持Groovy脚本的。有了Groovy脚本的支持,理论上其实我们能做的事情是无限多的。他就藏在我们可爱的Live Templates设置里面。

因此问题就变成了如何利用Groovy脚本来生成埋点的代码。显然,我们需要有埋点的一些信息。比如有哪些Event,Page,每个Page对应哪些Event等。看着显然包含这些信息但没有任何结构化的埋点doc文档,我的脑海里已经计划着要去勾搭哪个前端的汉子帮我搞定它。然而幸运的是,其实你想要的东西早就隐藏在某个角落里,只是你还没有发现它。

埋点管理平台

各个公司都有各自的埋点管理平台,埋点平台化之后我们就可以便捷提取结构化数据,类似数据库。

最终字节的埋点平台导出来的是一个excel文件,其中包含了埋点事件相关JSON格式的信息。对,你没看错。是JSON格式的。满满的幸福感~

当然,这里不局限于什么格式,我们可以根据每个公司现有埋点平台提供的基础能力,导出各自的结构化数据。重点是要把数据源,映射成相关埋点代码。

万事俱备

OK。现在我们所需要的一切都准备好了。接下来只需要ctrl+c, ctrl+v,啊不,我呸。接下来我们只要解析这个excel文件,并创建一个Live Template就行了。

解析Excel,生成辅助文件

1. 通过解析Excel文件,我们已经知道埋点所有的Page,Key,Event以及Value。在解析时就可以直接把这些信息填充到需要的类中。这一步我是用python实现的。

2. 生成一个Event和Key的映射关系文件

3. 生成一个Page和Event的映射文件

当然你也可以把2,3放在一个文件里面。我只是为了方便解析所以分开了。

创建一个Live Template

其中info的值为

groovyScript(“/Applications/Android\ Studio.app/Contents/plugins/android/lib/templates/pageEvent.groovy”, clipboard())

传入clipboard是为了接受我们当前想要埋点的事件属于哪个Page。于是,当我们在代码里输入page关键字后,就会去执行pageEvent.groovy脚本。这个脚本是我事先放在那里的(当然不是自己copy过去的。毕竟团队其他成员也要用它)。

pageEvent.groovy脚本的内容就不贴了。主要就是根据page名和我们之前生成的辅助文件,知道有多少个Event,每个Event对应哪些Key。然后按照一定规则拼接成代码段返回。

整体效果就是文章最开头贴的效果了。看起来一切都那么的美好,王子终于不用加班了,从此王子和公主幸福快乐的生活在一起了,直到永远。The End~

等等,我还得去上手动导出Excel?

懒是无止境的。我理解你。因为我也是。所以去埋点平台上手动导出Excel这种事情我是不会做的。这辈子我也不会做的。但是埋点平台又没有提供API给我调用。怎么办呢?嗯。。。这难不倒我。我还有selenium。通过模拟浏览器操作,我们顺利的解决了这个问题。虽然没有那么的优雅,但是比起被埋点支配的恐惧,这不算什么。

小贴士

为了简化埋点的代码,我们做了一个扩展方法。最终埋点的代码比较清晰,只需要关注Key和Value本身,而不需要各种繁琐的方法调用。

Track.Event.POINT_READING_SCALE.log(handler,
    Track.Key.STATUS to status,
    Track.Key.ACTION to action
)
复制代码

扩展方法代码如下

fun String.log(vararg pairs: Pair<String, Any>): Event {
    return log(null, *pairs)
}

fun String.log(handler: ITrackHandler?, vararg pairs: Pair<String, Any>): Event {
    val event =  Event.create(this).apply {
        pairs.forEach {
            val (key, value) = it
            params.put(key, value)
        }
    }
    event.log(handler)
    return event
}
复制代码

缺陷

这套流程在我们项目中经过数个版本的实践。整体上没有太大的问题。要说唯一的缺陷。。。大概就是推动DA在你埋点之前就在埋点平台上把埋点录入完成。嗯,我的建议是,如果你们的DA是男的,请派女的上。同理可得。

后记

藉由在埋点流程中的实践,本文对Live Templates如何在项目工程中发挥更大的作用进行了一部分探索。也希望各位大佬同事能够结合自己的经验分享更多的idea,提升开发效率。早点下班,( Ĭ ^ Ĭ )

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享