背景
BlockCanary主要提供监控Android UI卡顿,定位卡顿代码的能力。 本文通过分析框架源码,学习其设计原理和架构,内容包括:
- BlockCanary框架设计原理
- UI卡顿的监控和定位
- Android基础:设计模式/Handler/UI绘制原理
需求分析
- 开发者能收到UI发生卡顿的通知。
- 开发者能得到发生卡顿的内存快照。
- 了解造成卡顿的原因,并能进行模拟测试。
关键问题
在开始准备概要设计的时候,我们可以问自己几个问题,带着问题去看源码。
1. 用户为什么会感受到卡顿?
理论上屏幕画面需要以16ms每帧的速率刷新,用户会感到流畅。 而1 6ms每帧正对应60fps。这意味着Android系统底层的UI绘制框架尽可能地保障屏幕刷新率和GPU渲染速率相同。
而影响GPU渲染速率的因素包括:
- 主线程被阻塞。页面绘制产生图像数据发生阻塞,导致用户还是看到的上一帧画面。所以用户会抱怨:“很卡。。。"
- GPU硬件性能。
为了提高GPU渲染速率,这里,作为软件工程师,我们更需关注第一个因素:主线程什么情况下会被阻塞?
2. 主线程阻塞的原因有哪些?
1. IO阻塞
2. 线程等待阻塞
3. 密集CPU计算阻塞
3. 如何监控主线程阻塞?
由于Android的UI绘制和四大组件通信是在MainThread(主线程)进行,通过主线程中的Looper.loop(),循环调度内部消息队列中的消息执行。
如果主线程阻塞,那么会导致消息队列的消息得不到执行,如“页面的跳转“的消息就会得不到立即执行,产生卡顿。
如果我们能通过在源码埋点,统计Looper中消息队列的派发每个消息的间隔时间,是否能判断主线程发生了阻塞?
4.怎么得到卡顿时系统状态信息?
假设,我们已经发现了主线程阻塞。这个时候,我们需要得到当前系统的状态来还原卡顿现场,“破案”:
- 当前线程堆栈。以定位方法耗时最长的代码块。
- CPU负载。分析CPU是否高负荷,有可能是其他进程抢占CPU导致。
- 内存使用量。分析是否是内存不足,GC过快,产生线程阻塞。
- 总线程数和线程名。线程比较占用内存,排查是否有滥用线程池,同时可以线程名定位具体哪个线程死锁,阻塞等状态。
那么如何得到上面这些信息?
概要设计
带着上面四个关键问题,去看源码,通过调试和跟踪代码,我们可以得到BlockCanary框架的大致架构如下: 从上图可以看到框架分为UI层和逻辑层两个部分,用不同模块编译
详细设计
总结
|