iOS App卡顿监控(Freezing/Lag)

栏目: IOS · 发布时间: 6年前

内容简介:使用CFRunLoopObserverRef,实时获得这些状态值的变化,如下:添加计算逻辑,如下:目击卡顿现场,记录此时的调用函数信息,作为卡顿证据。

iOS App卡顿监控(Freezing/Lag)

如何判断主线程卡顿:

监测NSRunLoop耗时情况。

NSRunLoop的调用主要在 kCFRunLoopBeforeSourceskCFRunLoopBeforeWaiting 之间,以及 kCFRunLoopAfterWaiting 之后。因此,若是发现这个两个时间内耗时过长,就可以判定此时主线程出现卡顿情况。

一、监控NSRunLoop状态变化

使用CFRunLoopObserverRef,实时获得这些状态值的变化,如下:

/// RunLoop状态观察回调
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    <#MyClass#> *object = (__bridge <#MyClass#>*)info;
    // 记录状态值
    object->activity = activity;
}
/// 注册RunLoop状态观察
- (void)registerRunLoopObserver {
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                            kCFRunLoopAllActivities,
                                                            YES,
                                                            0,
                                                            &runLoopObserverCallBack,
                                                            &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}

二、RunLoop耗时计算

另外开启一个线程,实时计算两个状态区域之间的耗时,是否达到阈值。

dispatch_semaphore_t 让子线程更及时地获知主线程NSRunLoop状态变化

卡顿覆盖范围: 多次连续小卡顿 单次长时间卡顿

添加计算逻辑,如下:

/// RunLoop状态观察回调
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    <#MyClass#> *object = (__bridge <#MyClass#>*)info;
    // 记录状态值
    object->activity = activity;
    
    // 发送信号
    dispatch_semaphore_t semaphore = object->semaphore;
    dispatch_semaphore_signal(semaphore);
}
/// 注册RunLoop状态观察,并计算是否卡顿
- (void) registerRunLoopObserver {
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                            kCFRunLoopAllActivities,
                                                            YES,
                                                            0,
                                                            &runLoopObserverCallBack,
                                                            &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    
    // 创建信号
    semaphore = dispatch_semaphore_create(0);
    
    // 在子线程监控时长
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            // 假定连续5次超时50ms认为卡顿(当然也包含了单次超时250ms)
            long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC));
            if (st != 0) {
                if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting) {
                    if (++timeoutCount < 5) {
                        continue;
                    }
                    // 发现卡顿
                    NSLog(@"卡、卡、卡、顿、顿、了");
                }
            }
            timeoutCount = 0;
        }
    });
}

三、记录卡顿的函数调用

目击卡顿现场,记录此时的调用函数信息,作为卡顿证据。

此处,使用第三方Crash收集组件 PLCrashReporter ,它不仅可以收集 Crash信息 ,也可用于 实时获取各线程的调用堆栈 ,使用示例如下:

PLCrashReporterConfig *config = [[PLCrashReporterConfig alloc] initWithSignalHandlerType:PLCrashReporterSignalHandlerTypeBSD
                                                                   symbolicationStrategy:PLCrashReporterSymbolicationStrategyAll];
PLCrashReporter *crashReporter = [[PLCrashReporter alloc] initWithConfiguration:config];
NSData *data = [crashReporter generateLiveReport];
PLCrashReport *reporter = [[PLCrashReport alloc] initWithData:data error:NULL];
NSString *report = [PLCrashReportTextFormatter stringValueForCrashReport:reporter
                                                          withTextFormat:PLCrashReportTextFormatiOS];
NSLog(@"------------\n%@\n------------", report);

特别注意:

PLCrashReporter 虽然能提供较为准确的堆栈信息,用于定位问题,特别是使用符号化策略 PLCrashReporterSymbolicationStrategyAll

时,能够对堆栈信息进行符号化,但会消耗大量资源,需要占用较多时间,导致卡死现象(自测时,耗时超过7s,层多次到10s以上)。

不使用符号化策略 PLCrashReporterSymbolicationStrategyNone ,测试时,平均耗时也接近3s。

因此,加入该信息采集,需要特别注意,建议仅在开发调试阶段使用。

为了投入线上使用,还需要再想想如何解决该问题。

四、上报服务器

检测到卡顿,获取到调用堆栈信息,客户端再根据实际情况进行一定程度的过滤处理,将有价值的信息上报服务器。

后续对服务器收集到的数据进行分析,定位需要优化的功能逻辑。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming PHP

Programming PHP

Rasmus Lerdorf、Kevin Tatroe、Peter MacIntyre / O'Reilly Media / 2006-5-5 / USD 39.99

Programming PHP, 2nd Edition, is the authoritative guide to PHP 5 and is filled with the unique knowledge of the creator of PHP (Rasmus Lerdorf) and other PHP experts. When it comes to creating websit......一起来看看 《Programming PHP》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码