重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
一个好的产品离不开数据分析,在手机 APP 中,数据分析极致化需要细致到某个时刻列表曝光的了哪几个 Item。
我们提供的服务有:网站建设、成都网站制作、微信公众号开发、网站优化、网站认证、建宁ssl等。为上1000+企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的建宁网站制作公司
2022 年了,基本上目前 Android 上可以滑动的复杂列表都是 RecyclerView 或者其扩展,这里分享一个封装的思路。
如果非要细化细节:
各种方案核心都差不多,最关键的就是通过 LayoutManager 获取屏幕内第一个可见和最后一个可见 item position,上报其区间内的 Item。这里简称这个逻辑为 检查上报逻辑 。
但是触发时机有所不同,通常如下方案一和二所述,当然除了方案一和方案二外,还有一些别的方案,比如监听 RecyclerView 的布局树变化触发 检查上报逻辑 等方案。
可以发现方案二相比方案一更有利于减少各种回调的注册和周期的控制,下文会在方案二的基础上,阐述用法和相关实现思路。
仓库地址: RecyclerViewExposure
这里会主要说明一些主要逻辑,需要完整的逻辑可以 fork 仓库 查看
思路来自于 lifecycle 的设计,这里主要是想让 Activity/Fragment 提供可见和不可见的状态变化给外部订阅
对 List Item 的收集处理是 RecyclerViewExposure 最核心的收集数据逻辑,这里针对在 Activity 的使用作为例子。上文已经讲述如何做一个 PageLifeCycleHolder 为其他组件提供页面可见状态,下文将直接使用。
埋点是数据采集的专用术语,在数据驱动型业务中,如营销策略、产品迭代、业务分析、用户画像等,都依赖于数据提供决策支持,希望通过数据来捕捉特定的用户行为,如按钮点击量、阅读时长等统计信息。因此,数据埋点可以简单理解为:针对特定业务场景进行数据采集和上报的技术方案。
数据埋点非常看重两件事,一个是数据记录的准确性,另一个则是数据记录的完备性。
先讲数据的准确性。数据埋点非常强调规范和流程,因为参数的规范与合法,将直接影响到数据分析的准确性,如果准确性得不到保障,那么所有基于埋点得出的结论,都是不可信的。辛辛苦苦做了很久的方案,一旦因为一个疏忽的小问题,导致下游集中投诉,其实非常划不来。
道理每个人都懂,但现实情况中,数据埋点所面对的客观环境,其实非常复杂,例如:
因此本文有非常长的篇幅来写流程问题,其实是非常有必要的。
再讲数据的完备性。因为埋点主要是面向分析使用,对用户而言是个额外的功能,因此埋点的业务侵入性很强,很容易对用户体验造成影响。别的不说,仅仅是流量的消耗,就很容被用户喷。因此,要提前想清楚,我们要采集哪些东西,因为修改方案的成本,是伤不起的。
通常情况下,我们需要记录用户在使用产品过程中的操作行为,通过4W1H模型可以比较好的保障信息是完备的。4W1H包括:
规定好记录信息的基本方法之后,按照固定的频率,如每小时、每天,或者是固定的数量,比如多少条日志,或者是网络环境,比如在Wifi下上传,我们就可以开心的把埋点数据用起来了。
当然,数据记录的时效性也比较重要,但因为埋点数据通常量级会比较大,且各个端数据回传的时间不同,因此想做到实时统计,还是需要分场景来展开。在Flink技术日渐成熟的今天,全链路的实时采集与统计,已经不是什么难题。
在埋点的技术方案中,首先要重视的,是用户唯一标识的建设。如果做不到对用户的唯一识别,那么基础的UV统计,都将是错误的。
因此,在数据埋点方案中,有两个信息是一定要记录的,即设备ID+用户ID。设备ID代表用户使用哪个设备,如安卓的ANDROID_ID/IMEI,IOS中的IDFA/UDID,浏览器的Cookie,小程序的OpenID等。用户ID,代表用户在产品中所注册的账号,通常是手机号,也可以是邮箱等其他格式。
当这两个信息能够获得时,不论是用户更换设备,或者是同一台设备不同账号登录,我们都能够根据这两个ID,来识别出谁在对设备做操作。
其次,我们来看一下Web的数据采集技术。Web端数据采集主要通过三种方式实现:服务器日志、URL解析及JS回传。
浏览器的日志采集种类又可以分为两大类:页面浏览日志和页面交互日志。
除此之外,还有一些针对特定场合统计的日志,例如页面曝光时长日志、用户在线操作监控等,但原理都基于上述两类日志,只是在统计上有所区分。
再次,我们来看下客户端的数据采集。与网页日志对应的,是手机应用为基础的客户端日志,由于早期手机网络通讯能力较差,因而SDK往往采用延迟发送日志的方式,也就是先将日志统计在本地,然后选择在Wifi环境下上传,因而往往会出现统计数据延迟的情况。现如今网络环境好了很多,4G、5G流量充足,尤其是视频类APP基本上都是一直联网,因而很多统计能够做到实时统计。
客户端的日志统计主要通过SDK来完成,根据不同的用户行为分成不同的事件,“事件”是客户端日志行为的最小单位,根据类型的不同,可以分为页面事件(类比页面浏览)和控件点击事件(类比页面交互)。对于页面事件,不同的SDK有不同的方式,主要区别为是在页面创建时发送日志,还是在页面浏览结束后发送日志,区别在于业务统计是否需要采集用户的页面停留时长。
页面事件的统计主要统计如下三类信息:
埋点其实还需要考虑数据上传的方案,批量的数据可以通过Flume直接上报,流式的可以写到Kafka,或者直接使用Flink来处理。这些框架相关的内容不是本文考虑的重点,有兴趣的可以自行查阅资料。
有了指导思路和技术方案后,我们就可以着手制定相应的数据埋点流程规范了。
笼统上,流程规范会分成五个步骤,即需求评审、埋点申请、技术开发、埋点验证、发布上线。
第一步,需求评审。
前文提到过,数据埋点的方案一旦确定,返工和排查问题的成本都很高,但数据埋点之后的分析工作,又涉及到了PD、BI、算法、数据等多个角色。因此非常有必要,将需求内容和数据口径统一收口,所有人在一套口径下,将需求定义出来,随后业务侧再介入,进行埋点方案的设计和开发。
以前文提到的4W1H模型为例,常见的记录内容如下:
最后我们统计时,按照上述约定,统计用户在某个时间和地点中,看到了哪些信息,并完成了怎样的动作。上下游的相关人员,在使用这份数据时,产生的歧义或者是分歧,会小很多。
第二步,埋点申请
当下的热门应用,大多是以超级APP的形式出现,比如微信、淘宝、支付宝、抖音,超级APP会承载非常多的业务,因此技术方案上会十分统一。
因此,当我们的技术方案确定后,通常要在相应的埋点平台上,进行埋点申请。申请的内容包括分配的SPM、SCM码是什么,涉及到的平台是哪些,等等。SPM、SCM是什么,有什么用,同样可以自行查阅。
第三步,技术开发
当需求确定、申请通过后,我们就可以开始开发动作了,这里基本上是对研发同学进行约束。埋点的开发,简单讲,是分成行为埋点和事件埋点两个大类,每一类根据端的不同进行相应的开发。具体的技术方案详见前文01章节。
详细的设计规范,是需要留文档的,因为代码不能反应业务的真实意图,而不论是事后复盘与业务交接,都需要完整的文档来阐述设计思路。
第四步,埋点验证
埋点的验证很关键,如果上线后才发现问题,那么 历史 数据是无法追溯的。
验证有两种方式,一种是实时的功能验证,一种是离线的日志验证。
实时功能验证,指功能开发好后,在灰度环境上测试相应的埋点功能是否正常,比如点击相应的业务模块,日志是否会正确的打印出来。通常而言,我们需要验证如下三个类型的问题:
除去实时验证,我们也需要把日志写到测试环境中,查看数据上报的过程是否正确,以及对上报后的数据进行统计,侧面验证记录的准确性,如统计基本的PV、UV,行为、事件的发生数量。
很多时候,数据是需要多方验证的,存在一定的上下游信息不同步问题,比如对某个默认值的定义有歧义,日志统计会有效的发现这类问题。
第五步,发布上线。
应用的发布上线通常会有不同的周期,例如移动端会有统一的发版时间,而网页版只需要根据自己的节奏走,因此数据开始统计的时间是不同的。最后,应用应当对所有已发布的埋点数据,有统一的管理方法。
大多数时候,数据埋点的技术方案,只需要设计一次,但数据准确性的验证,却需要随着产品的生命周期持续下去,因此仅仅依靠人肉来准确性验证是不够的,我们需要平台来支持自动化的工作。埋点的准确性,大体有两种方法保障:一种是灰度环境下验证真实用户数据的准确性;另一种则是在线上环境中,验证全量数据的准确性。因此,发布上线之后,后续的管理动作,应该是对现有流程的自动化管理,因为团队大了,需要埋点的东西多种多样,让平台自己测试、自动化测试,就是很多测试团队必须走的路
移动互联网时代,无论是Android、iOS还是小程序,都有很多成熟的解决方案,无需花费很多的时间去处理埋点的事情,而且基于第三方提供的SDK进行埋点,在数据处理和分析上也有很大的优势。
但是在之前的PC互联网时代,除了网页端有百度统计、谷歌分析等,客户端的埋点似乎没有一套能拿出来可供大家讨论的解决方案,我就基于我的工作经验和理解,给大家分享一下PC客户端的埋点。
PC客户端的埋点
首先,在PC上,我们得知道我们需要统计些什么内容。
一个PC客户端,无论是工具类的还是内容类的,我们都希望知道我们提供的服务的效果。那么,我们从一个客户端安装、运行到最终被卸载来看看。
就拿产品使用较多的工具“Axure RP”来举例吧。如果“Axure RP”是我们自己的软件,首先我们需要知道被安装了,之后,我们关注激活情况,也就是使用,到最后,被卸载了,这一整个环节,构成了一个生命周期。 重点来了,对于这个生命周期,所有你想知道的关于“Axure RP”的情况你都可以统计到。
1.软件的安装
在PC客户端安装的过程中,流程一般是这样的:①运行安装包②弹出安装界面提供给用户操作③执行安装过程-写注册表、启动项、计划任务等④执行安装过程-创建安装的文件夹(③和④可以交换)。
在这个环节,我们一般需要知道:
安装包被运行了
在安装界面用户做了哪些操作
我们的安装过程是否正常执行
我们最终是否安装成功
在PC上,只要我们的安装包运行起来了,无论是弹出安装界面、写注册表还是创建文件,这些都是安装包可以控制的,所以我们能通过安装包进程,将整个安装环节的所有数据记录下来发送到我们的后台并记录下来 (这里要重点记住,由于安装是一次性的动作,所以统计一定要发实时的) 。
2.软件的使用
软件的使用,包括启动软件、使用功能和退出软件。
在PC上,软件的启动有很多种方式,例如开机自启动、计划任务、手动点击快捷方式,我们继续以“Axure RP”举例,当我们装上了“Axure RP”后,会在桌面、开始菜单中,创建快捷方式(有些程序会在任务栏上也创建),同时,会将后缀名为“rp”的文件默认打开方式调整为“Axure RP”。
对于启动, 我们就有了三种方式:桌面快捷方式、开始菜单快捷方式和默认软件打开,所以我们需要统计软件是否被启动了,是如何启动的。
对于使用功能, 当软件运行起来后,其进程就会启动,这个时候就跟移动端的应用类似,我们需要统计一系列事件,每个功能的使用情况、功能状态、付费、登录等一系列信息(区别于移动端的是,在PC上一般这些统计都是做单点统计,例如统计弹窗的弹出、功能的点击、某个状态,对于相互关联的一组事件统计是比较复杂的,需要定义结构体,在一条统计中包含很多组字段信息,因为没有成熟的SDK集成,所以基本都要自己定义埋点,复用性较差)。
这部分统计分为公共统计和专用统计。公共统计就是基本信息,常用的是用户标识、用户基本信息、计算机硬件信息和其他的可复用的;专用统计就是针对你的功能,你想了解哪些情况,针对性进行埋点统计。
对于软件退出, 这就比较简单了,是正常退出还是异常退出?软件使用了多久退出?
3.软件的卸载
软件卸载的流程包括启动卸载程序、用户操作、删除注册表及文件等操作、完成卸载。
在这个过程中,我们主要关注两方面的信息,一方面是用户怎么卸载的?是主动使用卸载程序,还是通过一些管理软件进行卸载;另一方面是用户为什么要卸载,这个时候我们可以在卸载的界面中给用户提供选择,以获取用户的反馈。
该怎么埋点
1.埋点的分类
(1)时效性
PC客户端一般情况下都比较复杂,子功能很多,可统计的内容很多,为了节省带宽,我们不可能每次都实时将数据传输回来,而且很多时效性不是很强的功能没有必要实时上报。
实时统计
当功能触发时或达到一定条件,立即将统计回传,一般情况下用于时效性比较强的功能,例如活跃统计、营收类统计,我们需要实时分析并调整策略。
延时统计
统计不立即回传,将统计积累,达到一定的条件或者一定的时间,统一将这部分统计回传,一般情况用于时效性不强的功能,例如采集设备信息、获取某些功能的状态、常规功能的统计,这部分统计使用范围比较广,一般都是隔日发送,有一天的延迟,统计的信息晚一天不会对分析产生较大的影响。
(2)埋点的作用
常规的基础统计
每次统计都需要发送,可以理解为公用统计,这部分统计是将几乎所有的统计都需要的部分包括进来,封装成一个统一的部分,每次发送统计都会带上这些内容,方便管理,节省后续埋点时间。
功能统计
针对特定功能,当功能被使用或者生效的时候,我们需要统计效果或者状态,可以理解为专用统计,不同于移动端,PC一般没有第三方提供的SDK,需要每个专用统计自己埋点,维护大量的统计内容,不过在一个公司内部,可以统一设计规范,方便维护。
(3)数据类型
结构体
统计连贯的事件,各项信息之间的关联很重要。
计数
统计某个行为发生的次数。
字符串
统计内容。
整形
统计数值,也可用来统计状态。
布尔型
统计需要判断的类型,一般使用场景较少,为了方便计算,大部分被整形和字符串替代。
2.数据埋点实例
(1)软件安装
场景:统计安装过程中的信息
(2)软件的使用
场景:软件启动后,用户使用了分享功能,将自己做的原型分享到了云端,最后用户关闭了软件。
要注意的是,软件启动和关闭,看需要是可以调整的,如果你只是想知道是不是启动了,来判断活跃,那么仅仅需要启动的时候发送个整型的值标识即可;如果想知道更详细的信息,比如启动方式、启动时间等等,可以定义结构体,将这一刻更多的信息发送回来,可灵活定义。
(3)软件卸载
卸载跟软件安装类似,这里就不赘述了。
在这里,如果希望收集用户的卸载原因,可以定义一个字符串,将用户填写的内容上报,这种形式的数据如果太多,不太利于分析,所以看产品情况可灵活设置。
应用的启动速度缓慢这是很多开发者都遇到的一个问题,比如启动缓慢导致的黑屏,白屏问题,大部分的答案都是做一个透明的主题,或者是做一个Splash界面,但是这并没有从根本上解决这个问题。那么如何从根本上解决这个问题或者做到一定程度的缓解?
1、冷启动:当启动应用时,后台没有该应用的进程,这时系统会首先会创建一个新的进程分配给该应用,这种启动方式就是冷启动。
2、热启动:当启动应用时,后台已有该应用的进程,比如按下home键,这种在已有进程的情况下,这种启动会从已有的进程中来启动应用,这种启动方式叫热启动。
3、温启动 :当启动应用时,后台已有该应用的进程,但是启动的入口Activity被干掉了,比如按了back键,应用虽然退出了,但是该应用的进程是依然会保留在后台,这种启动方式叫温启动。
adb shell am start -W [PackageName]/[PackageName.MainActivity]
执行成功后将返回三个测量到的时间:
这里面涉及到三个时间,ThisTime、TotalTime 和 WaitTime。WaitTime 是 startActivityAndWait 这个方法的调用耗时,ThisTime 是指调用过程中最后一个 Activity 启动时间到这个 Activity 的 startActivityAndWait 调用结束。TotalTime 是指调用过程中第一个 Activity 的启动时间到最后一个 Activity 的 startActivityAndWait 结束。如果过程中只有一个 Activity ,则 TotalTime 等于 ThisTime。
总结:如果只关心某个应用自身启动耗时,参考TotalTime;如果关心系统启动应用耗时,参考WaitTime;如果关心应用有界面Activity启动耗时,参考ThisTime。
从我们Application开始到首页显示出来,这个过程,我们应该注意一些什么,将这个过程细分一下,会有下面的时间点需要注意。
Application的构造器方法——attachBaseContext()——onCreate()——Activity的构造方法——onCreate()——配置主题中背景等属性——onStart()——onResume()——测量、布局、绘制显示在界面上。
因为上面这些阶段全部都是在主线程中执行的,任何不经意的操作都可能拖慢应用的启动速度。所以我们不应在Application以及Activity的生命周期回调中做任何费时操作,具体指标大概是你在onCreate,onResume,onStart等回调中所花费的总时间最好不要超过400ms,否则用户在桌面点击你的应用图标后,将感觉到明显的卡顿。但是有些 不得以的任务 又必须在UI显示之前执行。所以我们要将 任务 划分优先级。
对于首页渲染完成后,开始加载,或者延迟加载,延迟加载的目的就是界面先显示出来,然后加载,但是你觉得要延迟多久呢?在 Android 的高端机型上,应用的启动是非常快的 , 这时候只需要 Delay 很短的时间就可以了, 但是在低端机型上,应用的启动就没有那么快了,而且现在应用为了兼容旧的机型,往往需要 Delay 较长的时间,这样带来体验上的差异是很明显的。延迟加载有一种方式。
极力推荐用第二种,在窗口完成以后进行加载,这里面的run方法是在onResume之后运行的。关于这种懒加载机制,参考 Android应用启动优化:一种DelayLoad的实现和原理(上篇) ,给出了详细的解释。
通过上面我们知道一种懒加载机制,所以我们可以将Application中和首页的onCreate中的有些耗时任务,放到首页渲染完毕后加载。如何找出这些耗时任务,TraceView就派上用场了,TraceView的用法,移步我的前面的博客 Android性能优化第(六)篇---TraceView 分析图怎么看?
比如在首页的onCreate中我们进行了用户启动上报,这个进行懒加载是不是分分钟减少139毫秒呢?
在比如在Application里面用到了GSON,将String转化成json,我将这个移动到懒加载里面,是不是又减少了100毫秒呢?
在比如,有些Application中做了支付SDK的初始化,用户又不会一打开App就要支付,放在Application中加载干嘛?
此处我们这里举得例子是优化了139毫秒和100毫秒的,其实真正耗时的任务有的有1秒多,都被我优化完了,所以trace图中看不到了,就举个了这两个例子,还有SharedPreferences也是耗时大户,经过检测保存一个boolean变量耗时120+毫秒以上。
利用TraceView可以清楚我们每一个方法的耗时时间,极大的帮助了我们做优化工作。
五、优化思路总结
1、UI渲染优化,去除重复绘制,减少UI重复绘制时间,打开设置中的GPU过度绘制开关,各界面过度绘制不应超过2.5x;也就是打开此调试开关后,界面整体呈现浅色,特别复杂的界面,红色区域也不应该超过全屏幕的四分之一;
2、根据优先级的划分,KoMobileApplication的一些初始化工作能否将任务优先级划分成3,在首页渲染完成后进行加载,比如:PaySDKManager。
3、主线程中的所有SharedPreference能否在非UI线程中进行,SharedPreferences的apply函数需要注意,因为Commit函数会阻塞IO,这个函数虽然执行很快,但是系统会有另外一个线程来负责写操作,当apply频率高的时候,该线程就会比较占用CPU资源。类似的还有统计埋点等,在主线程埋点但异步线程提交,频率高的情况也会出现这样的问题。
4、检查BaseActivity,不恰当的操作会影响所有子Activity的启动。
5、对于首次启动的黑屏问题,对于“黑屏”是否可以设计一个.9图片替换掉,间接减少用户等待时间。
6、对于网络错误界面,友好提示界面,使用ViewStub的方式,减少UI一次性绘制的压力。
7、任务优先级为2,3的,通过下面这种方式进行懒加载的方式
8、Multidex的使用,也是拖慢启动速度的元凶,必须要做优化。后面有空专门写一篇Multidex。
相关链接:
Android应用启动优化:一种DelayLoad的实现和原理(上篇)
Android性能优化之加快应用启动速度
手机淘宝性能优化全记录
Android客户端性能优化(魅族资深工程师毫无保留奉献)
Please accept mybest wishes for your happiness and success !