APP冷啟動以及頁面加載時間

當(dāng)我們的 App 大到一定規(guī)模時,就需要開始關(guān)注應(yīng)用的啟動時間了,因?yàn)檫@關(guān)系到用戶體驗(yàn)問題。

我們通常說的啟動時間為:用戶點(diǎn)擊應(yīng)用圖標(biāo)蛮放,顯示閃屏頁,到該應(yīng)用首頁界面被加載出來的總時間(冷啟動)奠宜,對于 iOS App 來說包颁,啟動時間包括兩部分:Launch Time = Pre-main Time + Loading Time,如下圖所示压真,其中:

  • Pre-main Time 指 main 函數(shù)執(zhí)行之前的加載時間娩嚼,包括 dylib 動態(tài)庫加載,Mach-O 文件加載榴都,Rebase/Binding待锈,Objective-C Runtime 加載等;

  • Loading Time 指 main 函數(shù)開始執(zhí)行到 AppDelegateapplicationDidBecomeActive: 回調(diào)方法執(zhí)行(App 被激活)的時間間隔嘴高,這個時間包含了的 App 啟動時各初始化項(xiàng)的執(zhí)行時間(一般寫在 application:didFinishLaunchingWithOptions: 方法里)竿音,同時包含首頁 UI 被渲染并顯示出來的耗時和屎。

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

Loading Time
對于第二個時間 Loading Time,比較好測量春瞬,我們可以在 main 函數(shù)開始執(zhí)行和 applicationDidBecomeActive: 方法執(zhí)行末尾時分別記錄一個時間點(diǎn)柴信,然后計(jì)算兩者時間差即可,大致如下:
//在main函數(shù)加上 [[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 添加環(huán)境變量 DYLD_PRINT_STATISTICS 并把其值設(shè)為 1,如下圖:

image.png

這樣我們就可以在編譯運(yùn)行工程時萄涯,在控制臺看到 Total pre-main time 總耗時了

頁面加載時間

如果想統(tǒng)計(jì)每個頁面的加載時間绪氛,我的處理方式是HOOK每個控制器的loadView 方法 和 viewDidAppear方法 在這兩個方法中分別記錄時間,每個頁面的加載時間就是viewDidAppear的加載時間減去loadView里的記錄時間
注:這里推薦一個好用的hook庫 Aspects

這里貼出普通頁面的時間統(tǒng)計(jì) 如果我們想統(tǒng)計(jì)每個頁面的加載時間 我們需要有一個基類涝影,所有的控制器都繼承于這個基類

pragma mark 普通頁面使用統(tǒng)計(jì)

- (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:--- %@ --- 關(guān)閉",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:--- %@ --- 關(guān)閉",clazz);
                                                       
                                                       NSDate *date = classInfo[@"classUseDate"];
                                                       [weakSelf.normalVCUse removeObject:classInfo];
                                                       if (date ) {
                                                           NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:date];
                                                           /*
                                                            當(dāng)使用過程中程序進(jìn)入后臺并停留一段時間時枣察,統(tǒng)計(jì)時長需要減去該段時間
                                                            */
                                                           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 中加入以下代碼
 // 初始化并配置統(tǒng)計(jì)工具
    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;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市燃逻,隨后出現(xiàn)的幾起案子序目,更是在濱河造成了極大的恐慌,老刑警劉巖伯襟,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猿涨,死亡現(xiàn)場離奇詭異,居然都是意外死亡姆怪,警方通過查閱死者的電腦和手機(jī)叛赚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來片效,“玉大人红伦,你說我怎么就攤上這事〉硪拢” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵召调,是天一觀的道長膨桥。 經(jīng)常有香客問我,道長唠叛,這世上最難降的妖魔是什么只嚣? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮艺沼,結(jié)果婚禮上册舞,老公的妹妹穿的比我還像新娘。我一直安慰自己障般,他們只是感情好调鲸,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布盛杰。 她就那樣靜靜地躺著,像睡著了一般藐石。 火紅的嫁衣襯著肌膚如雪即供。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天于微,我揣著相機(jī)與錄音逗嫡,去河邊找鬼。 笑死株依,一個胖子當(dāng)著我的面吹牛驱证,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恋腕,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼抹锄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吗坚?” 一聲冷哼從身側(cè)響起祈远,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎商源,沒想到半個月后车份,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牡彻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年扫沼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庄吼。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡缎除,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出总寻,到底是詐尸還是另有隱情器罐,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布渐行,位于F島的核電站轰坊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祟印。R本人自食惡果不足惜肴沫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蕴忆。 院中可真熱鬧颤芬,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間讨盒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工存谎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肥隆。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓既荚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親栋艳。 傳聞我的和親對象是個殘疾皇子恰聘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344