美文网首页
APP冷启动以及页面加载时间

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

作者: Jackxu_q | 来源:发表于2019-11-07 10:20 被阅读0次

    当我们的 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> 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> 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> 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冷启动以及页面加载时间

          本文链接:https://www.haomeiwen.com/subject/vfumbctx.html