前端埋点方案研究及在Android项目中的实践

article/2025/10/8 8:42:29

前言

数据埋点的质量直接关系到前端监控、数据分析结果的准确性,对应用的用户行为分析、数据分析决策、数据化运营、错误分析来说都是基础的存在,在前端监控与数据分析中是第一个重要的步骤。中本文聚焦于目前主流埋点方案的探究,以及其在Android项目中的具体实践。

埋点数据

数据埋点首先必须解决的,是需要埋什么的问题。

  • 站在产品的角度,埋点应该是页面停留时长、用户分享、用户支付等具体的用户行为,或者如网络连接变化、用户上下线等用户状态的变化等
  • 在前端开发的角度,埋点就是按钮点击、页面开启、页面关闭、错误信息捕获、网络状态变化广播接收等一系列的事件
  • 在数据分析的角度,埋点则是对这些事件的点进行清洗、筛选、分析,进而形成一系列的事件线,也就是如页面停留时长、用户支付路径等一系列产品或业务需要的用户行为事件。
    通常一个完整的事件组成结构如下:
    在这里插入图片描述
    在Android项目实践中,由于国家与各平台的隐私合规相关规定,ip、定位信息、Android设备唯一标记(mac、androidId等)已不再使用,可改为使用有盟OAID、工信部统一推送ID、自定义算法计算等方式,确定Android设备的唯一性。
    需要注意的是,卸载重装App可能会改变此类方法获取的设备唯一标识,因此只适合用来做确定设备唯一性的指标之一。

Android元素事件监听

在正式开始分析主流埋点方式在Android中的实践前,我们需要预先了解一下Android主流的用于埋点的事件监听机制,主要有Listener代理、Hook、AccessibilityDelegate三种监听方式,对于埋点而言,业务代码侵入性越低越能接受。下面以点击事件为例简单描述:

Listener代理

最简单的方式就是直接将元素的点击事件监听器等替换为自定义监听器。

view.setOnClickListener(object : ProxyListener() {@Overridepublic void doOnClick(View view) {// 原View.OnClickListener.onClick事件}
})abstract class ProxyListener : View.OnClickListener{@Overridepublic void onClick(View view) {track() // 点击埋点逻辑doOnClick(view) }abstract void doOnClick(View view)
}

反射Hook

通过反射获取activity的Rootview,遍历View树,将元素的OnClickListenr对象,替换为包裹了了原OnClickListener逻辑的OnClickListener对象,进而实现自动注入埋点逻辑。Hook时机可选择在Application启动时使用生命周期注入ActivityLifecycleCallbacks,获取activity对象,并在渲染完时完成注入。

{...// 反射得到ListenerInfo对象val getListenerInfo = View::class.java.getDeclaredMethod("getListenerInfo")getListenerInfo.isAccessible = trueval mListenerInfo = getListenerInfo.invoke(view)// 获取原始的OnClickListenerval listenerInfoClz = Class.forName("android.view.View\$ListenerInfo")val mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener")mOnClickListener.isAccessible = trueval originOnClickListener: View.OnClickListener =  mOnClickListener.get(mListenerInfo) as View.OnClickListener// 用Hook代理类替换原始的OnClickListenerval hookedOnClickListener = HookedClickListenerProxy(originOnClickListener)mOnClickListener.set(mListenerInfo, hookedOnClickListener)....
}class HookedClickListenerProxy(val originOnClickListener: View.OnClickListener):View.OnClickListener {override fun onClick(v: View?) {track() // 点击埋点逻辑originOnClickListener.onClick(v)}
}

此种Hook可以做到无需代码开发实现埋点,同时使用ActivityLifecycleCallbacks提供了配置化埋点的选择。

AccessibilityDelegate

利用辅助功能可检测控件点击,选中,滑动,文本等属性变化,具体流程与反射Hook基本一致,不同的是,将反射替换元素的OnClickListenr,改为设置元素的AccessibilityDelegate。
setAccessibilityDelegate同样可以做到无需代码开发实现埋点,同时使用ActivityLifecycleCallbacks提供了配置化埋点的选择,且没有反射Hook的属性反射消耗。

object TrackAccessibilityDelegate : View.AccessibilityDelegate() {override fun sendAccessibilityEvent(host: View?, eventType: Int) {super.sendAccessibilityEvent(host, eventType)if (eventType == AccessibilityEvent.TYPE_VIEW_CLICKED) {host?.let {track() // 点击埋点逻辑}}}
}view.setAccessibilityDelegate(TrackAccessibilityDelegate)

前端主流埋点方案

有盟统计、博睿数据、神策数据、GrowingIO等一众数据采集分析平台,提供了方便快捷的应用监控与分析功能,而对数据安全比较重视,业务又相对复杂的公司则通常会有自己的一套前端监控与分析平台。无论是自研还是各分析平台,其主要的前端埋点技术无外乎三种:代码埋点、可视化埋点、无埋点。

代码埋点

最常用的埋点方式,由前端开发手动将事件数据采集的代码,加入到原有的业务代码中。
一般的代码埋点方式是直接在业务代码中,插入埋点代码段:

class XXActivity : BaseActivity() {// 页面开启埋点override fun onResume() {super.onResume()addTrackCode(code = TrackCode.CODE_1,eventType = "page",visitType = VISIT_TYPE_OPEN)}// 页面离开埋点override fun onPause() {super.onPause()addTrackCode(code = TrackCode.CODE_1,eventType = "page",visitType = VISIT_TYPE_LEAVE)}// 点击按钮、状态改变等业务埋点fun onStateChanged() {addTrackCode(code = TrackCode.CODE_12,eventType = "btn",eventParam = arrayOf(mapOf("code" to "11112")))}
}

鉴于代码埋点侵入性较强,在Android实际应用中,可以利用生命周期注入ActivityLifecycleCallbacks与注解,将部分埋点逻辑抽取,以减少代码侵入:

// 页面进出埋点被简化
@TrackPage(code = TrackCode.CODE_1)
class XXActivity : BaseActivity() {// 点击按钮、状态改变等业务埋点fun onXXXClick() {addTrackCode(code = TrackCode.CODE_12,eventType = "btn",eventParam = arrayOf(mapOf("code" to "11112")))}
}/*** activity、Fragment页面埋点(进入、离开)* 在activity onCreated前插入Fragment的页面埋点插入*/
object TrackActivityLifecycleCallbacksImpl : ExtActivityLifecycleCallbacks() {override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {// 利用生命周期函数,在Lifecycle.Event.ON_CREATE时主动读取@TrackPage注解,完成页面进入埋点// 在Lifecycle.Event.ON_DESTROY时完成页面离开埋点(activity as? LifecycleOwner)?.lifecycle?.addObserver(TrackLifecycleObserver)// Fragment页面也一样处理(activity as? FragmentActivity)?.supportFragmentManager?.registerFragmentLifecycleCallbacks(TrackFragmentLifecycleCallbacksImpl, true)}
}/*** 注入activity公共逻辑*/
object TrackLifecycleObserver : LifecycleObserver {@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)fun onLifecyclePause(owner: LifecycleOwner?) {owner?.apply {addTrackPageCode(owner, false)}}@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)fun onLifecycleResume(owner: LifecycleOwner?) {owner?.apply {addTrackPageCode(owner, true)}}/*** 使用声明的TrackPage code,插入埋点*/private fun addTrackPageCode(obj: Any, isOnResumed: Boolean) {obj::class.java.getAnnotation(TrackPage::class.java)?.code?.let {addTrackCode(code = it,eventType = "page",visitType = if (isOnResumed) VISIT_TYPE_OPEN else VISIT_TYPE_LEAVE)}}
}

代码埋点需要开发人员根据埋点需求,侵入性地在业务代码中埋点,此方法埋点位置精确,可以携带丰富的业务参数,但另一方面埋点开发工作量大,同时随着版本的迭代,部分页面或逻辑调整后,业务埋点的增删改会也会导致代码维护的困难。

可视化埋点

可视化埋点方案一般采用第三方平台sdk,可支持原生、web、react native等多平台,集成之后,通过可视化工具配置采集节点,在App/Web端解析配置,查找节点,监听节点产生的事件并上报到平台。神策数据可视化埋点是比较典型可视化埋点应用:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可视化埋点的实现由两个重要步骤实现:

  • 录制:通过可视化工具配置采集的View,目的是保存一份需埋点页面或元素的配置到后台,一般有两种实现方式:
    1. 神策数据使用app内嵌sdk,使app需埋点页面与后台管理界面同步,同时在管理界面上选择埋点配置
    2. GrowingIO的sdk采用圈选操作,app拖动圆点到需要监测的元素上,进而设置埋点配置
  • 采集:app解析上一步产生的的配置,hook相应页面或元素的事件,并上报数据

录制配置

录制配置时app与平台后端一般都是使用WebSocket连接,用于传输当前页面层次结构与执行埋点指令。
Android app中,sdk获取通过反射获取当前activity的RootView,进而通过遍历RootView得到View树,以及所有子View的所需属性(id、大小、位置、类型、子View等),同时通过截图Api createSnapshot获取当前页面截图,将截图与View树信息一并传给平台后端,平台后端则可根据这两份数据在还原页面结构,同时保存相应的元素关键信息。
操作人员在平台后端根据埋点需求,在相应元素指定埋点配置,例如按钮btn点击时,上报点击事件,同时可以携带上页面可见元素的部分信息,如某文本框上的文字等。此时就形成了一个明确的埋点配置,简略数据结构可如下:

  • target_activity:元素所在的activity类名
  • event_type:事件类型,例如点击事件
  • event_name:事件名称
  • path:在View树中路径
    • view_class:View的类名
    • index:在父View里的下标
    • id:View在Apk中的id

根据配置采集

sdk在启动时,会先下载录制配置步骤产生的所有埋点配置,根据这份埋点配置,可以使用代码埋点中的ActivityLifecycleCallbacks,在相应页面activity渲染完成时,使用id或遍历View树等,查找到需要埋点的元素。通过使用设置AccessibilityDelegate方式,给元素设置事件以及相应参数,在相应事件触发时,埋点就能被记录下来。

小结

不同于代码埋点需要开发人员进行大量的侵入性埋点,以及后续的相应代码维护,可视化埋点可按需由非技术人员完成,可以相对自由地在基本所有页面完成页面元素加载、元素点击事件的埋点。需要注意的是,可视化埋点在各平台基本都是属于收费服务,自建平台的话,需要重点解决录制配置的web平台、各端所使用到的sdk以及两者之间的通信机制。

无埋点

无埋点是指将所有的页面开关、所需的元素事件全部记录并上报后台,由数据分析后台清洗、分析数据再进行业务埋点,其中上章中的反射Hook、AccessibilityDelegate两种动态Hook技术均可用于实现无埋点。因为动态Hook使用反射,所以对运行效率会有一定的影响。
除了动态Hook,还可以使用面向切面的AOP静态Hook技术,在编译阶段,将埋点代码段直接“写入”到业务代码中,也不需要进行额外的埋点开发。静态Hook有两种主流的方式:

  • AspectJ:Android使用aspectjx实现AspectJ,通过注解切面,将埋点代码插入元素事件或特定函数方法的前面或后面。但是aspectjx与项目所使用到的阿里热更组件sophix以及hilt框架等均有冲突,此处不考虑
  • ASM:通过在编译阶段修改字节码文件完成插入埋点逻辑,性能好,但是有一定的上手难度
    在这里插入图片描述
    ASM修改字节码所需的知识储备略多,此处以插入点击事件埋点为例,简述几个关键点:
    1、gradle插件编写
    Android项目实现ASM无埋点最假的方式是使用gradle插件,因此需要预先新建插件项目,并提供插件参数配置,以过滤需要进行埋点的代码,如排除类、符合类、埋点方法规则等。
class TrackPlugin : Plugin<Project> {override fun apply(project: Project) {// 注册埋点插桩val androidComponentsExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)androidComponentsExtension.onVariants { variant ->variant.instrumentation.apply {// 控制哪些.class需要被扫描transformClassesWith(TrackClassVisitorFactory::class.java, InstrumentationScope.ALL) {// 此处可以对接gradle配置参数it.ignoreClassM.set(listOf("*.XXXBaseActivity", "*.XXXFragment"))}setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES)}}}
}

2、Transform Api
Transform Api将在gradle 8.0后被移除,需要使用TransformAction替代,AGP提供的AsmClassVisitorFactory极大地方便了我们使用Transform Action对.class文件进行ASM操作:

class TrackClassVisitorFactory(override val parameters: Property<TrackParams>, override val instrumentationContext: InstrumentationContext) : AsmClassVisitorFactory<TrackParams> {// 对符合条件的类,执行插桩override fun createClassVisitor(classContext: ClassContext,nextClassVisitor: ClassVisitor): ClassVisitor = TrackClassNode(nextClassVisitor)// true时createClassVisitor才有效,此处可以过滤需要插桩的类override fun isInstrumentable(classData: ClassData) = parameters.get().ignoreClassM.get().indexOfFirst {  Pattern.matches(it, classData.className) } < 0
}/*** 定义排除类、符合类、埋点方法规则等*/
interface TrackParams : InstrumentationParameters {/*** 需要忽略的类,此处仅作示例*/@get:Inputval ignoreClassM: ListProperty<String>
}

简化后的埋点代码是写到onClick方法前面的,因此需要先使用ClassNode过滤出需要执行操作的类,并且过滤出onClick方法,将其交由相应的方法处理器TrackMethodVisitor:

class TrackClassNode(private val classVisitor: ClassVisitor) : ClassNode(Opcodes.ASM9) {// ASM Tree API会把class文件包装成ClassNode方便操作override fun visitMethod(access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>?): MethodVisitor {return if (name == "onClick") {// 示例,在点击时插桩埋点代码val mv = super.visitMethod(access, name, descriptor, signature, exceptions)TrackMethodVisitor(mv)} else {super.visitMethod(access, name, descriptor, signature, exceptions)}}
}

方法处理器中插入埋点代码:

class TrackMethodVisitor(private val methodVisitor: MethodVisitor) : MethodVisitor(Opcodes.ASM9) {override fun visitCode() {super.visitCode()// 方法执行前插入埋点代码methodVisitor.visitFieldInsn(GETSTATIC, "com/bluemoon/configplugin/TrackUtil", "INSTANCE", "Lcom/bluemoon/configplugin/TrackUtil;")methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/bluemoon/configplugin/TrackUtil", "track", "()V", false)}
}

可以借助ASM Bytecode Viewer插件的ASMifield选项查看ASM操作代码,同时可以对比插入埋点代码前后的ASM操作代码,得出需要在相应方法中插入的操作码
在这里插入图片描述
3、发布插件
编译后完成后,ASM无埋点插件就做好了,可使用maven发布插件以供项目使用,此处不赘述。

小结

全埋点可以实现收集所有的页面、元素事件收集,同时对业务开发无侵入,但是庞大的埋点数据,需要强大的数据分析平台清洗筛选以及分析处理。一般支持全埋点的数据分析平台,都是有最长数据保存时限,若不及时转化成报表,会导致无法回溯历史数据。

埋点方式小结对比

埋点方式优点缺点适用场景
代码埋点1、埋点位置灵活,可精确记录业务事件,几乎可以覆盖所有数据采集场景
2、可携带丰富详细的业务参数
3、可灵活控制上报时机,可进行灵活数据预处理
1、埋点开发工作量大
2、代码侵入性大,业务变更可能需要重新开发埋点,维护困难
3、埋点数据未上传前存在客户端,非即时上传,有丢失可能(主动删除)
1、埋点需要把行为与业务数据充分结合分析,如支付
2、埋点位置不适合自动化配置,如监听业务状态变化、页面滑动等
可视化埋点1、业务可直接视图化选取埋点位置,简单方便
2、可主动开启或关闭埋点
3、开发只需要接入sdk,无需额外维护
1、埋点数据区分版本,历史数据无法回溯
2、无法采集业务相关数据与自定义数据,只能采集点击、元素展示等客户端行为基本事件
快速迭代只需简单统计PV、UV等指标,或业务场景简单的产品
无埋点1、开发维护简单,只需集成SDK,后续改版也不影响采集
2、覆盖全面,所有的点击、页面开启关闭、元素状态回调等页面元素信息都可采集
1、数据全采集,事件数据量大,后端存储压力大,可预先规定埋点数据预处理方案,压缩数据
2、工作方式决定了无埋点无法采集动态生成的页面或元素事件
3、无法采集业务相关数据与自定义数据
1、业务场景简单的产品
2、自建数据监控与分析平台,需要进行精细化业务分析,并可解决埋点数据量庞大的问题

总结

目前Android项目主要使用友盟移动统计进行用户数据基本分析,友盟APM与腾讯buggly结合进行异常监控,使用优化后的代码埋点框架进行业务埋点。由于友盟统计越来越多的功能开始收费,同时随着项目业务数据分析需求的定制化发展,我们也需要对前端数据采集、监控与分析进行一步步的迭代发展,本地数据预处理优化后的ASM无埋点+优化的代码埋点框架+第三方数据分析平台辅助是一种比较全面的选择。

参考资料

用户行为数据采集:常见埋点方案优劣势对比及选型建议
Android ASM插桩
Android埋点技术分析
Transform 现如今被废弃,ASM 该如何适配


http://chatgpt.dhexx.cn/article/5R4RH3nY.shtml

相关文章

前端监控和前端埋点方案设计

在线上项目中,需要统计产品中用户行为和使用情况&#xff0c;从而可以从用户和产品的角度去了解用户群体&#xff0c;从而升级和迭代产品&#xff0c;使其更加贴近用户。用户行为数据可以通过前端数据监控的方式获得&#xff0c;除此之外&#xff0c;前端还需要实现性能监控和异…

903前端埋点 springboot

显示用户偏好效果 ajax代码效果 accessrandomfile 创建springboot工程 spring:datasource:driver-class-name: oracle.jdbc.driver.OracleDriverurl: jdbc:oracle:thin:192.168.100.151:1521:orclusername: cmpassword: okmybatis:mapper-locations: classpath*:mapper/*.xmlty…

前端埋点学习笔记

目的 埋点是为了采集用户行为数据&#xff08;例如页面访问路径&#xff0c;点击了什么元素&#xff09;&#xff0c;便于进行数据分析。 三大类型 现有的埋点采集方案可以大致被分为三种 手动埋点 需要调用埋点的业务方在需要采集数据的地方调用埋点的方法&#xff0c;可以…

浅谈前端埋点监控

大家好&#xff0c;我是若川。持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 加我微信lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外&…

【埋点】前端埋点打点库数据统计库

前端埋点sdk 前言埋点包括在IOS、Android、H5、小程序等前端埋点&#xff0c;也包括后端业务埋点。这里仅仅讲讲这些年和产品经理、运营撕逼上百个回合的前端埋点内容。 一、buried-point-sdk是什么&#xff1f;二、使用和例子1.如何使用(一)、第一种:umd方式(1)、引入文件(2)、…

web前端埋点及数据上报

web前端埋点及数据上报 简介 前端埋点即在产品客户端获取用户行为和使用情况的一种监控方式。通过埋点可以获取到用户行为数据&#xff0c;借助这些数据&#xff0c;我们可以从用户角度出发&#xff0c;升级迭代产品&#xff0c;使其更加贴近用户使用习惯&#xff0c;提升产品…

【前端埋点方案】前端监控和前端埋点方案介绍

在线上项目中,需要统计产品中用户行为和使用情况&#xff0c;从而可以从用户和产品的角度去了解用户群体&#xff0c;从而升级和迭代产品&#xff0c;使其更加贴近用户。用户行为数据可以通过前端数据监控的方式获得&#xff0c;除此之外&#xff0c;前端还需要实现性能监控和异…

埋点tracker:前端埋点服务-技术要点梳理

一、背景 埋点方案&#xff0c;前端涉及到哪些技术要点&#xff0c;本文做简单的梳理和总结。 二、指纹追踪技术&#xff1a;识别到用户及设备 浏览器&#xff1a;浏览器指纹_snowli的博客-CSDN博客 三、用户设备信息&#xff08;navigator&#xff09; navigator.userAgent 四…

前端 埋点

App.vue是vue页面资源的首加载项&#xff0c;是主组件&#xff0c;页面入口文件。所有页面都是在App.vue下进行也换的&#xff0c;APP.vue 负责构建定义及页面组件归集 需求 1.打开页面的时候&#xff0c;记录当前页面 2.切换页面的时候&#xff0c;记录在当前页面的停留时间…

前端埋点实现方案

前言 领导今天又来活了&#x1f623;&#xff0c;要记录每个页面的停留时间&#xff0c;以及页面的操作&#xff0c;是由哪个页面跳转过来的&#xff0c;给每个页面生成GUID上报给服务端&#xff0c;并且需要携带设备型号和设备唯一标识&#x1f644; 名称解释 UV&#xff0…

前端组件化埋点方案与实现

背景 埋点&#xff0c;是收集产品的数据的一种方式&#xff0c;其目的是上报相关行为数据&#xff08;PV/UV/时长/曝光/点击等&#xff09;&#xff0c;由相关人员以此分析用户的使用习惯&#xff0c;助力产品不断迭代和优化。对于开发来说&#xff0c;通常不仅仅需要完成基础…

三分钟,教你3种前端埋点方式!

作者&#xff1a;彩虹修狗 https://juejin.cn/post/7224132741997281338 前言 只有了解用户&#xff0c;我们才能服务好用户&#xff0c;而最接近用户的我们&#xff0c;自然要承担起更多的责任。 那么在一个企业中&#xff0c;我们要如何去了解用户呢&#xff1f;最直接有效的…

前端数据埋点

数据埋点&#xff0c;是一种常用的数据采集方法。埋点是数据的来源&#xff0c;采集的数据可以帮助业务人员分析网站或者App的使用情况、用户行为习惯等&#xff0c;是后续建立用户画像、用户行为路径等数据产品的基础。 前端的埋点方式主要分为代码埋点、可视化埋点、无埋点三…

前端埋点实现

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 前端埋点实践 介绍1. 实现自定义hook&#xff0c;监测组件2. 收集数据3.前端错误捕捉4. 发送后端保存数据5.收集数据展示总结 介绍 这段时间博主一直在投入…

前端埋点实现及原理分析

正如在宏观介绍的博客中写到的&#xff0c;做用户行为分析的方式有“前端埋点”和“后端埋点”的区分&#xff0c;真好今天敲了一个坤哥整理的“前端埋点”的程序&#xff0c;理解了之后结合demo来简单讲解“前端埋点”如何做。 前端埋点原理图&#xff1a; 如上所示&#xff…

FReLU

论文&#xff1a;https://arxiv.org/pdf/2007.11824.pdf 代码&#xff1a;https://github.com/megvii-model/FunnelAct 概述 卷积神经网络&#xff08;CNN&#xff09;在许多视觉识别任务&#xff08;例如图像分类&#xff0c;目标检测和语义分割&#xff09;中均达到了最先进…

LFR benchmark在windows操作系统下形成网络详细步骤

研究社交网络的人应该都知道LFR benchmark network吧。但是我从网上找到很多关于LFR的压缩包&#xff0c;里面包含很多.cpp文件&#xff0c;在vs下建工程&#xff0c;把这些文件放进去却怎么也跑不通&#xff0c;真的是很恼火。 今天&#xff0c;终于看到一篇博客&#xff0c;…

神经网络-LFR model

CLDNN[1] 不同的网络结构有不同的优势 CNN擅长减少频率偏移LSTM擅长对时序信号进行建模DNN可以对特征做更高阶的抽象&#xff0c;更容易进行分类 CLDNN依次将CNN/LSTM/DNN进行串联组合成一个新的网络&#xff0c;相当于依次进行频域变化/时域关联/特征抽象&#xff0c;相比于…

CRLF和LF区别

目录&#xff1a; 文章目录 1、什么是CRLF和LF2、为什么要探究CRLF和LF3、三种方式处理的不同4、在Git中如何转换&#xff1f;参考文献 1、什么是CRLF和LF CRLF 是carriagereturnline feed的缩写。中文意思是回车换行。 LF是line feed的缩写&#xff0c;中文意思是换行。 2、…

LFR benchmark 操作步骤

先奉上资源 链接&#xff1a;https://pan.baidu.com/s/1Mm_UwUAhM0ofKXcFbti0YA 提取码&#xff1a;hvp8 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦运行操作 在解压后的文件下找到 /benchmark/Debug 文件夹&#xff0c; 在该文件下运行命令行程序&#x…