APP冷启动以及页面加载时间

当我们的 App 大到一定规模时,就需要开始关注应用的启动时间了,因为这关系到用户体验问题。

我们通常说的启动时间为:用户点击应用图标,显示闪屏页,到该应用首页界面被加载出来的总时间(冷启动),对于 iOS App 来说,启动时间包括两部分:Launch Time = Pre-main Time + Loading Time,如下图所示,其中:

  • Pre-main Time 指 main 函数执行之前的加载时间,包括 dylib 动态库加载,Mach-O 文件加载,Rebase/Binding,Objective-C Runtime 加载等;

  • Loading Time 指 main 函数开始执行到 AppDelegateapplicationDidBecomeActive: 回调方法执行(App 被激活)的时间间隔,这个时间包含了的 App 启动时各初始化项的执行时间(一般写在 application:didFinishLaunchingWithOptions: 方法里),同时包含首页 UI 被渲染并显示出来的耗时。

屏幕快照 2019-11-07 上午10.04.43.png

Loading Time
对于第二个时间 Loading Time,比较好测量,我们可以在 main 函数开始执行和 applicationDidBecomeActive: 方法执行末尾时分别记录一个时间点,然后计算两者时间差即可,大致如下:
//在main函数加上 [[XYYAPMLoadMonitor shareManager]startAPPOpenTime];

image.png

code10以及以下
//在AppDelegate didFinishLaunchingWithOptions的第一行 中加入 [[XYYAPMLoadMonitor shareManager]appInitTime];
image.png

//在AppDelegate didFinishLaunchingWithOptions的最后一行 中加入 [[XYYAPPStartUpMonitorManager shareManager]firstVCLoadDoneTime];


image.png

Xcode11(生命周期交给UIWindowScene来管理)
需要在- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions方法里 相同位置注入上面的代码

Pre-main Time

而对于第一个时间 Pre-main Time,目前没有比较好的人工测量手段,好在 Xcode 自身提供了一个在控制台打印这些时间的方法:在 Xcode 中 Edit Scheme -> Run -> Auguments 添加环境变量 DYLD_PRINT_STATISTICS 并把其值设为 1,如下图:

image.png

这样我们就可以在编译运行工程时,在控制台看到 Total pre-main time 总耗时了

页面加载时间

如果想统计每个页面的加载时间,我的处理方式是HOOK每个控制器的loadView 方法 和 viewDidAppear方法 在这两个方法中分别记录时间,每个页面的加载时间就是viewDidAppear的加载时间减去loadView里的记录时间
注:这里推荐一个好用的hook库 Aspects

这里贴出普通页面的时间统计 如果我们想统计每个页面的加载时间 我们需要有一个基类,所有的控制器都继承于这个基类

pragma mark 普通页面使用统计

- (void)pagesUsingStatisticWithArray:(NSArray *)array{
    __block __weak typeof(self) weakSelf = self;
    // screen views tracking
    for (NSDictionary *trackedScreen in array) {
        Class clazz = NSClassFromString(trackedScreen[@"className"]);
        //页面开始加载时间
        [clazz aspect_hookSelector:@selector(loadView)
                       withOptions:AspectPositionAfter
                        usingBlock:^(id aspectInfo) {
                            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                                           ^{
                                               NSString *className = NSStringFromClass([aspectInfo.instance class]);
                                               if (weakSelf.openLog){
                                                   //NSLog(@"aspectInfo:%@",NSStringFromClass([aspectInfo.instance class]));
                                                   NSLog(@"className:--- %@ --- 页面开始加载",className);
                                               }
                                               
                                               NSDictionary *classInfo = @{@"className":className,
                                                                           @"pageName":trackedScreen[@"pageName"],
                                                                           @"pageStartDate":[NSDate date]};
                                               [weakSelf.pageStartTimes addObject:classInfo];
                                           });
                        }
                             error:nil];
        
        
        [clazz aspect_hookSelector:@selector(viewDidAppear:)
                       withOptions:AspectPositionAfter
                        usingBlock:^(id aspectInfo) {
                            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                                           ^{
                                               
                                               NSArray *pageDrutions = [NSArray arrayWithArray:weakSelf.pageStartTimes] ;
                                               for (NSDictionary *classInfo in pageDrutions) {
                                                   NSString *className = NSStringFromClass([aspectInfo.instance class]);
                                                   if ([classInfo[@"className"] isEqualToString:className]) {
                                                       //                                                       NSLog(@"className:--- %@ --- 关闭",clazz);
                                                       
                                                       NSDate *date = classInfo[@"pageStartDate"];
                                                       //long dateTimeInterVal = [weakSelf getDateTimeTOMilliSeconds:date];
                                                       [weakSelf.pageStartTimes removeObject:classInfo];
                                                       if (date ) {
                                                           //                                                           long long currentTimeInterVal = [weakSelf getDateTimeTOMilliSeconds:[NSDate date]];
                                                           //                                                           long long duration = currentTimeInterVal - dateTimeInterVal;
                                                           NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:date]*1000;
                                                           if (weakSelf.openLog){
                                                               NSLog(@"className:--- %@ --- 页面启动时间  pageStartDrutionDate: %f豪秒",className,duration);
                                                           }
                                                           
                                                           [weakSelf markStatisticLogWithLogName:className Duration:duration];
                                                       }
                                                       
                                                   }
                                               }
                                               
                                               
                                               NSString *className = NSStringFromClass([aspectInfo.instance class]);
                                               if (weakSelf.openLog){
                                                   //NSLog(@"aspectInfo:%@",NSStringFromClass([aspectInfo.instance class]));
                                                   NSLog(@"className:--- %@ --- 页面开始完成",className);
                                               }
                                               
                                               NSDictionary *classInfo = @{@"className":className,
                                                                           @"pageName":trackedScreen[@"pageName"],
                                                                           @"classUseDate":[NSDate date]};
                                               [weakSelf.normalVCUse addObject:classInfo];
                                           });
                        }
                             error:nil];
        
        
        
        SEL selektor = NSSelectorFromString(@"viewDidDisappear:");
        [clazz aspect_hookSelector:selektor
                       withOptions:AspectPositionBefore
                        usingBlock:^(id aspectInfo) {
                            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                                           ^{
                                               
                                               NSArray *normalVCDrutions = [NSArray arrayWithArray:weakSelf.normalVCUse];
                                               for (NSDictionary *classInfo in normalVCDrutions) {
                                                   Class cls = [aspectInfo.instance class];
                                                   NSString *className = NSStringFromClass(cls);
                                                   if ([classInfo[@"className"] isEqualToString:className]) {
                                                       //                                                       NSLog(@"className:--- %@ --- 关闭",clazz);
                                                       
                                                       NSDate *date = classInfo[@"classUseDate"];
                                                       [weakSelf.normalVCUse removeObject:classInfo];
                                                       if (date ) {
                                                           NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:date];
                                                           /*
                                                            当使用过程中程序进入后台并停留一段时间时,统计时长需要减去该段时间
                                                            */
                                                           if (self.backDate && ([self.backDate timeIntervalSinceDate:date] > 0)) {
                                                               duration = duration - [self.aliveDate timeIntervalSinceDate:self.backDate];
                                                           }
                                                           if (weakSelf.openLog){
                                                               NSLog(@"className:--- %@ --- 用户使用并停留  useTimeDuration: %.2f秒",className,duration);
                                                           }
                                                           
                                                           [weakSelf markStatisticLogWithLogName:className Duration:duration];
                                                       }
                                                       
                                                   }
                                               }
                                               
                                           });
                        }
                             error:nil];
    }
}
//在AppDelegate  didFinishLaunchingWithOptions 中加入以下代码
 // 初始化并配置统计工具
    XYYAPMLoadMonitor *manager = [XYYAPMLoadMonitor shareManager];
   
[manager setupWithTabbarControllerNames:@[@"XYYHomeViewController",@"XYYAllDrugsViewController",@"XYYFoundViewController",@"XYYShoppingCartViewController",@"XYYMeViewController"] controllers:@[@"XYYBaseController"] appDelegate:@"XYYAppDelegate"];
    
    [manager setupWithTabbarControllerNames:@[] controllers:@[@"XYYBaseController"]];
    manager.logStrategy = XYYLogSendStrategyCustom;
    manager.enableExceptionLog = NO;
    manager.logSendInterval = 1;
    manager.openLog = NO;
    manager.enableMonitor = NO;

你可能感兴趣的:(APP冷启动以及页面加载时间)