Rabbit是目前我正在开发的一个框架,它主要用来提高App开发的效率和质量,总体定位上偏向于一个APM框架。

统计应用冷启动时长、页面渲染时长是APM系统不可缺少一个功能。Rabbit中这个功能的实现主要参考自Android自动化页面测速在美团的实践,目前已经完成下面功能点:Application.onCreate耗时统计

应用冷启动耗时统计

Activity.onCreate耗时统计

Activity初次inflate耗时统计

Activity初次渲染耗时

页面网络请求耗时监控

具体统计时机如下图:

测速组件名词解释.png

最终输出的效果如下:应用启动测速

app_speed.png页面启动测速

page_render_speed.png网络耗时测速

page_request_speed.png

使用方法

整个测速组件实现的思路主要是:利用Gradle插件在应用编译时动态注入监控代码。因而使用时需要在应用的build.gradle中应用插件:apply plugin: 'rabbit-tracer-transform'

为了支持网络监控功能,需要在OkHttpClient初始化时插入阻拦器(目前只支持OkHttp的网络监控):OkHttpClient.Builder().addInterceptor(Rabbit.getApiTracerInterceptor())后面会考虑把Interceptor的初始化做成AOP的方式。

除此之外Rabbit的测速功能不需要其余的初始化代码,接下来就大概过一下上面功能的实现原理:

应用onCreate耗时统计

实现思路:编译应用时在Application.attachBaseContext()开始和Application.onCreate()结束方法中插入耗时统计代码。

SDK收集测速数据,而后展现。对于编译时的字节码插入本文就不做详细实现分析,具体实现可以参考Rabbit源码中的实现,最终插入效果如下:public class CustomApplication extends Application { protected void attachBaseContext(Context base) { AppStartTracer.recordApplicationCreateStart(); super.attachBaseContext(base); } public void onCreate() { super.onCreate(); Rabbit.init(this); AppStartTracer.recordApplicationCreateEnd(); }}

页面渲染耗时统计

什么时候才算页面渲染完成呢?

Rabbit定义Activity的ContentView绘制完成就是页面渲染完成,我们可以通过监听ViewGroup.dispatchDraw()来监听Activity.ContentView绘制完成。

具体实现思路是: 手动为Activity.setContentView()设置的View增加一层自己设置父View,用于计算绘制完成的时间public class ActivitySpeedMonitor extends FrameLayout { @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); RabbitTracerEventNotifier.eventNotifier.activityDrawFinish(getContext(), System.currentTimeMillis()); } public static void wrapperViewOnActivityCreateEnd(Activity activity) { FrameLayout contentView = activity.findViewById(android.R.id.content); ViewGroup contentViewParent = (ViewGroup) contentView.getParent(); if (contentView != null && contentViewParent != null) { ActivitySpeedMonitor newParent = new ActivitySpeedMonitor(contentView.getContext()); if (contentView.getLayoutParams() != null) { newParent.setLayoutParams(contentView.getLayoutParams()); } contentViewParent.removeView(contentView); newParent.addView(contentView); contentViewParent.addView(newParent); } }}

上面ActivitySpeedMonitor.wrapperViewOnActivityCreateEnd()代码会在编译时插入在Activity.onCreate方法中:public class TransformTestActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { ActivitySpeedMonitor.activityCreateStart(this); super.onCreate(savedInstanceState); this.setContentView(2131296286); ActivitySpeedMonitor.wrapperViewOnActivityCreateEnd(this); }}

Activity初次inflate耗时统计

我们知道ViewGroup.dispatchDraw()方法在ViewTree发生改变时就会调用,而一般第一次会导致dispatchDraw()被调用代码是:setContentView(R.layout.activity_transform_test);

因而Rabbit将Activity的第一dispatchDraw()方法完成时间当做Activity初次Inflate结束时间点。其实这个时间的长短可以代表Activity的布局复杂度。

Activity初次渲染耗时

这个耗时统计的时间结束点为: 页面发起网络请求拿到数据,并完成页面渲染

举个例子,比方你的应用首页有3个接口,这3个接口的数据组成了整个首页的UI, 首页的渲染耗时就是3个接口完成请求,并且数据渲染完成。

Rabbit中对页面的渲染耗时统计需要配置,即配置一个页面哪些接口完成才算页面渲染完成, 具体配置商定为在assest文件夹下提供rabbit_speed_monitor.json文件:{ "home_activity": "MainActivity", "page_list": [ { "page": "MainActivity", "api": [ "xxx/api/getHomePageRecPosts", "xxx/api/getAvailablePreRegistrations", "xxxx/api/appHome" ] } ... ]}home_activity配置统计应用冷启动耗时。page_list配置需要统计渲染耗时的页面。

Rabbit会在指定的所有接口都完成并且ViewGroup.dispatchDraw()方法完成时记录下这个时间点来作为渲染耗时:RabbitAppSpeedMonitor.javafun activityDrawFinish(activity: Any, drawFinishTime: Long) { val apiStatus = pageApiStatusInfo[currentPageName] if (apiStatus != null) { if (apiStatus.allApiRequestFinish()) { //所有请求已经完成 pageSpeedCanRecord = false //只统计一次 pageSpeedInfo.fullDrawFinishTime = drawFinishTime RabbitDbStorageManager.save(pageSpeedInfo) } } }

如何统计接口完成呢?

网络请求耗时监控

也是利用RabbitAppSpeedInterceptor,不过这里监控的网络耗时时间并不是我们真正了解的网络请求耗时,时间大概介于 : 网络请求耗时 ~ 应用网络解决耗时,具体实现核心代码如下:class RabbitAppSpeedInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val startTime = System.currentTimeMillis() val request = chain.request() val requestUrl = request.url().url().toString() val response = chain.proceed(request) if (!RabbitTracer.monitorRequest(requestUrl)) return response // 不需要监控这个请求 val costTime = System.currentTimeMillis() - startTime RabbitTracer.markRequestFinish(requestUrl, costTime) return response }}

App冷启动耗时统计

结合上面的叙述,Rabbit定义App冷启动耗时为HomeActivity渲染完成时 - Application.attachBaseContext()开始时。

对于HomeActivity可以通过rabbit_speed_monitor.json进行配置:{ "home_activity": "MainActivity", "page_list": [ ... ]}

总结

应用测速组件的实现原理并不是很复杂,不过还是涉及到了很多点。具体实现逻辑可以参考 : RabbitRabbit中目前使用的统计时机可能并不是最合适的,假如你知道更合适的统计时机,欢迎交流。Rabbit功能的实现原理见:Rabbit实现原理剖析

Android测速代码,Android应用测速组件实现原理相关推荐

  1. android小球移动代码,Android中如何绘制一个跟随手指移动的小球

    Android中如何绘制一个跟随手指移动的小球 发布时间:2020-11-07 16:22:43 来源:亿速云 阅读:82 作者:Leah 本篇文章为大家展示了Android中如何绘制一个跟随手指移动 ...

  2. android小球移动代码,Android自定义圆形View实现小球跟随手指移动效果

    本文实例为大家分享了Android实现小球跟随手指移动效果的具体代码,供大家参考,具体内容如下 一. 需求功能 手指在屏幕上滑动,红色的小球始终跟随手指移动. 实现的思路: 1)自定义View,在on ...

  3. android调频收音机代码,android 收音机 FM 驱动 hal层 框架层以及应用层代码

    [实例简介] android 收音机 FM 驱动 hal层 框架层以及应用层代码 方法一 不需要framework部分 1.fm放到 \hardware\rk2x 2.FmRadio 放到 packa ...

  4. Android钢琴滑动代码,android 钢琴界面实现

    近在做一个钢琴的东西,关于这个界面如何设计画了很长时间,主要是考虑到针对不同的分辨率,如果只针对一种分辨率的话用绝对布局可以实现,实现的基本思想是每个白色的键的位置是可以计算出来的,屏幕的宽度可以获得 ...

  5. android确认密码代码,Android自定义View实现验证码or密码输入框

    前言 最近项目中有支付功能,用户输入密码时要类似微信支付密码输入框的样式,本想直接copy网上的,但设计姐姐总是对样式挑三拣四,抽空自己自定义了一个,无奈之下抽空自定义了个,并把它贴到GitHub上供 ...

  6. android相对布局代码,Android基础_3 Activity相对布局(示例代码)

    相对布局要比前面讲的线性布局和表格布局要灵活一些,所以平常用得也是比较多的.相对布局控件的位置是与其周围控件的位置相关的,从名字可以看出来,这些位置都是相对的,确定出了其中一个控件的位置就可以确定另一 ...

  7. android确认密码代码,Android手机卫士之确认密码对话框

    本文接着实现"确认密码"功能,也即是用户以前设置过密码,现在只需要输入确认密码 布局文件和<Android 手机卫士--设置密码对话框>中的布局基本类似,所有copy一 ...

  8. android 图片切割代码,Android用clip剪切图像资源

    使用clip剪切图像资源可以做进度条,图像慢慢展开的效果,他使用以下几个属性来控制效果: android:drawable:指定要剪切的原图像. android:clipOrientation:截取的 ...

  9. android退出app代码,Android应用退出代码各种方式

    1. 点击按钮最小化app返回到home //并不会退出app,只是最小化 findViewById(R.id.button3).setOnClickListener(new OnClickListe ...

  10. android物理健代码,Android 物理按键整理及实例代码

    先给大家看一段代码,然后说明. import android.app.Activity; import android.os.Bundle; import android.util.Log; impo ...

最新文章

  1. php类方法语法错误捕获,php语法错误捕获
  2. 阅读论文《一种金融市场预测的深度学习模型FEPA》(1)
  3. shell脚本 转 二进制执行文件
  4. java基础篇之理解synchronized的用法
  5. boost::multi_array模块实现打印数组相关的测试程序
  6. 不知道怎么用GitHub怎么当程序员?拿出十分钟,包你会
  7. oracle表ddl,七、Oracle中DDL改变表结构操作
  8. Python 成仙之路
  9. 由数据范围反推算法复杂度
  10. 用计算机数字语言研究中医理论,试论中医基础理论数字模型.pdf
  11. linux内核下载 ok6410,手把手教你移植linux内核---------OK6410(一)
  12. 阿卡索口语学习(Learn And Talk 0)短语及单词(二)
  13. 东方财富:资金流向表爬虫
  14. Fiddler原理+雷电模拟器进行APP抓包
  15. Monitor(管程)是什么意思?Java中Monitor(管程)的介绍
  16. cadence allegro - PCB设计规范
  17. 80c51汇编语言程序案例指导,新第4章80C51的汇编语言程序设计
  18. 《 ThinkSNS 社区服务接口文档.md 》
  19. Google 宣布废弃 LiveData.observe 方法
  20. 华三服务器管理口地址_h3c 的交换机怎样设置管理地址?

热门文章

  1. mxh缩写英语_mxht的含义,mxht是什么的缩写,mxht的词语,mxht代表的意思
  2. 2022茶艺师(中级)考试题及答案
  3. SPSS——方差分析
  4. ps图片放大模糊怎么变清晰?
  5. 多线程(Thread的类的运用-Runnable类的使用/多线程的注意点)
  6. 标题: Excel地址 Excel单元格的地址表示很有趣,它使用字母来表示列号。 比如, A表示第1列, B表示第2列, Z表示第26列, AA表示第27列, AB表示第28列, BA表示第53列
  7. win10计算机管理界面模糊,Win10电脑屏幕显示模糊
  8. 邮箱不能发送大附件,什么邮箱可以发送超大附件?
  9. 样本峰度(kurtosis)与随机变量的峰度及四阶统计量之间的关系和计算估计
  10. 全球及中国细胞和基因治疗用融化设备行业发展模式及未来前景分析报告2022-2028年