iOS轻量级主线程卡顿、卡死监控工具类
通过监听runloop状态,设置阈值来判定主线程是否出现卡顿或者卡死的情况。
1、流畅性与丢帧:动画、滑动列表不流畅,一般为十几至几十毫秒的级别
2、卡顿:短时间操作无反应,恢复后能继续使用,从几百毫秒至几秒
3、卡死:长时间无反应,直至被系统杀死,通过线上收集数据,最少为5s
iOS开发中,由于UIKit是非线程安全的,因此一切与UI相关的操作都必须放在主线程执行,系统会每16ms(1/60帧)将UI的变化重新绘制,渲染至屏幕上。如果UI刷新的间隔能小于16ms,那么用户是不会感到卡顿的。但是如果在主线程进行了一些耗时的操作,阻碍了UI的刷新,那么就会产生卡顿,甚至是卡死。主线程对于任务的处理是基于Runloop机制,如下图所示。Runloop支持外部注册通知回调,提供了
1、RunloopEntry
2、RunloopBeforeTimers
3、RunloopBeforeSources
4、RunloopBeforeWaiting
5、RunloopAfterWaiting
6、RunloopExit
6个时机的事件回调,其流转关系如下图所示。Runloop在没有任务需要处理的时候就会进入至休眠状态,直至有信号将其唤醒,其又会去处理新的任务。
在日常编码中,UIEvent事件、Timer事件、dispatch主线程任务都是在Runloop的循环机制的驱动下完成的。一旦我们在主线程中的任何一个环节进行了一个耗时的操作,或者因为锁的使用不当造成了与其它线程的死锁,主线程就会因为无法执行Core - Animation的回调而造成界面无法刷新。而用户的交互又依赖于UIEvent的传递和响应,该流程也必须在主线程中完成。所以说主线程的阻塞会导致UI和交互的双双阻塞,这也是导致卡死、卡顿的根本原因。
目前大多数APM工具都是采用监听Runloop的方式进行卡顿的捕获,这也是性能、准确性表现最好的一种方案。HangMonitor就是通过监听Runloop的方式。
卡顿:HangMonitor默认连续三次超过80ms&小于5s runloop处于kCFRunLoopBeforeSources或者kCFRunLoopAfterWaiting状态,视为卡顿。
卡死:HangMonitor默认大于等于5s runloop处于kCFRunLoopBeforeSources或者kCFRunLoopAfterWaiting状态,视为卡死。
卡顿和卡死阈值均可以根据自己的业务进行修改。
To run the example project, clone the repo, and run pod install from the Example directory first.
#import "BGHangMonitor.h"
// 监听卡顿回调
BGHangMonitor.shareInstance.hangBlock = ^(NSString * _Nonnull vcClassName, double hangT) {
NSLog(@"------%@发现卡顿,卡顿时长%fs-----", vcClassName, hangT);
};
// 监听卡死回调
BGHangMonitor.shareInstance.deadBlock = ^(NSString * _Nonnull vcClassName) {
NSLog(@"------%@发现卡死,主线程无响应时长>5s-----", vcClassName);
};
// 设置卡顿间隔时间 默认80ms(连续3次都超过了卡顿间隔视为卡顿)
BGHangMonitor.shareInstance.hangT = 80;
// 设置卡死阈值 默认5s
BGHangMonitor.shareInstance.deadT = 5;
// begin monitor
[BGHangMonitor.shareInstance beginMonitor];// log
FJViewController发现卡顿,卡顿时长4.194800s
// log
FJViewController发现卡死,主线程无响应时长>5s#import "BGHangMonitor.h"HangMonitor is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'HangMonitor'jin fu, [email protected]
HangMonitor is available under the MIT license. See the LICENSE file for more info.