美文网首页
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冷启动以及页面加载时间

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

  • 移动4G网络App页面加载时间过长

    参考原文@移动4G网络App页面加载时间过长前言 移动4G网络App页面加载时间过长解决办法 一...

  • 闪屏页的几种写法

    Android的冷启动时间? 冷启动时间是指用户从手机桌面点击APP的那一刻起到启动页面的Activity调用on...

  • App冷启动优化

    冷启动定义 冷启动定义: 从用户点击App到首屏展示为止。 T1:main()函数之前,即操作系统加载App可执行...

  • 页面间跳转的性能优化(一)

    前言 现在App的页面越来越复杂,页面初始化的工作越来越多,加载页面所需的时间也随之增长,如果页面加载的时间...

  • iOS 启动优化(上)

    相关概念 App 启动分类 冷启动 内存中不包含App的相关数据,必须从磁盘加载到内存(即 App 被 kill ...

  • Android 冷启动秒开实现

    【APP冷启动秒开】:冷启动是指应用杀掉进程时的启动;秒开是指点击桌面APP的图标后立即显示启动页面(PS:这个启...

  • App页面加载

    1.单页面整体加载 一次性加载完页面的全部内容 2.页面分块加载 优先加载重要内容、分页加载 3.跨页面预加载 帮...

  • 性能优化07-启动优化

    01冷启动简介 启动时间就是点击app图标到app将数据显示到屏幕上面的时间 APP的启动可以分为2种冷启动(Co...

  • iOS App启动优化方案

    冷启动 热启动:系统里面存在APP的进程缓存信息,比如杀掉APP后短时间内重启APP。 冷启动:系统里面没有APP...

网友评论

      本文标题:APP冷启动以及页面加载时间

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