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> 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;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335