一種新的頁面載入時間檢測方式

貝加爾湖畔的老樹發表於2019-02-21

背景

在一次組內會議中,被分配到了這樣一個技術研究需求,目的是通過檢測頁面載入耗時,來對頁面進行鍼對性的優化.拿到這個任務之後,立馬去搜集了一些網上現有的資料,並作出了一些總結.

目前實現檢測的幾種方式

基本思路

通常是利用swizllingviewDidLoad方法裡儲存一個初始時間,然後在viewDidAppear裡得到頁面出現的時間.

@implementation UIViewController (LoadTime)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       swizzleMethod([UIViewController class], @selector(viewDidLoad), @selector(my_viewDidLoad));
    });
       swizzleMethod([UIViewController class], @selector(viewDidAppear), @selector(my_viewDidAppear:));
    });
}

- (void)my_viewDidLoad
{
    NSDate *date = [NSDate date];
    // 儲存開始時間
    _date = date;
    [self my_viewDidLoad];
}
- (void)my_viewDidAppear:(BOOL)animated{
    [self my_viewDidAppear:animated];
    // 得到載入時間
    NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:_date];
    NSLog(@"Page %@ cost %g to be appeared", [self class], duration); 
}

@end
複製程式碼

一種基於KVO的頁面載入時間獲取(作者五子棋)

原文在這裡

這篇部落格指出了上述方法的問題,你hook的其實是父類UIViewController的方法,子類其實是呼叫了[super xxxxxx]方法,這種處理方式沒辦法對每種頁面都進行處理,需要各自建立對應分類. 於是作者突發奇想,利用KVO拿到派生的子類進行IMP的替換,從而解決了這個問題.以上兩種方式只能得出程式碼載入時間,如果某些頁面和網路有關,網路請求這部分時間就很難拿到了.

「無侵入頁面載入完成檢測」的一些思路(作者Limboy)

原文在這裡

這個方法是我完全沒有想到的一種處理方式.利用影象純色佔比來判斷當前頁面是否是載入完成.簡單來說,就是開啟一個CADisplayLink定時器,對當前頁面進行截圖,然後利用計算純色佔比的演算法算出比例,當比例大於某一個閾值,就說明頁面已經載入成功了.這種方法我覺得是最直觀的方法,但作者也列舉了一些問題:

1.需要主動去截圖檢測,而不能載入完成後告知。這其中的差別在於無法得知具體哪個時間載入完成了。
2.有些頁面被故意設計成有較多留白,這時就不容易判斷了。
3.「未載入完成」不同的頁面會有不同的表現。
4.當使用者滑動時,有可能之前的頁面已經載入了

美團Hertz的思路

原文在這裡

這篇文章介紹了美團關於效能監控的一些措施,也提到了iOS中頁面載入時間檢測的方式:在iOS中我們採取了不同的做法,Hertz在配置檔案中指定最終渲染頁面的某個元素的tag,並在網路請求成功後開啟CADisplayLink檢查該元素是否出現在根節點下面。

總結下來的三個問題
  • 問題1:即使解決了無法直接hook子類的實現,但是也不能得到確切的載入時間如下面的例子:
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    // 模擬了一個非同步網路請求
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(3);
        dispatch_async(dispatch_get_main_queue(), ^{
            TempView *tView = [[TempView alloc] init];
            tView.frame = CGRectMake(10, 20, 300, 200);
            tView.backgroundColor = [UIColor orangeColor];
            [self.view addSubview:tView];
        });
    });
}
複製程式碼

在這個例子中,使用者所感受到的載入完成應該是tView在網路請求之後顯示的時間.單純的hook生命週期方法是無法獲取網路延遲這段時間的.

  • 問題2:如果開啟定時器進行頁面檢測截圖,消耗記憶體的同時也會影響頁面渲染,可能會造成效能問題.
  • 問題3:美團這種思路是可行的,但是這個tag該怎麼打呢,如果是一個純tableView的控制器顯示該怎麼判定呢,webview呢?.

新的思路

這裡我借鑑了美團的思路,但又有所不同.

我們先弄清楚2個問題:

1.我們檢測頁面載入時長的目的主要是為了檢測某些頁面從載入到顯示的時間,通常頁面的出現除了頁面自身的渲染出現,還伴隨著介面資料的重新整理,有些介面依賴網路請求,有些介面依賴本地讀取或者直接顯示靜態頁面.
2.並不是所有的介面都需要進行檢測,我們應該把監測重點放到一些使用者常用的介面上,當然,能覆蓋越多越能發現更多可能的問題.

所以我們只需要檢測某個控制器中的某個關鍵子view出現,就可以確定這個時間.那我們怎麼判斷這些子頁面真的顯示呢?

  • 如果該頁面主要子view是UITableView或者UICollectionView,那麼可以指定找到這個頁面的這兩種子類,通過visibleCells是否大於0來判斷.
  • 如果該頁面主要子view的型別是其他view,那麼根據這個view是否出現在螢幕上來判斷.
  • 如果該頁面主要子view型別是webview,那麼可以根據webview是否載入url並且是否loading來判斷(有更好的方式可以告訴我).

知道如何判斷頁面顯示了,那麼我們需要一個配置檔案,來指定那些頁面關鍵子view的型別和其它屬性.為了能夠靈活配置,建議通過後臺介面下發一個json,當然也可以本地配置一個dictionary. 我這裡的檔案格式如下:

/*
 TargetSubview:關鍵子view
 TargetSubviewType:子view的型別  0:UITableView/UICollectionView 1:NormalView 2:Webview
 TargetEmptyViewType:可能會有的空白view型別
 */
@"ViewController":@{
            @"TargetSubview" : @"UITableView",
            @"TargetSubviewType" : @(0),
            @"TargetEmptyViewType":@"NoDataView"
            },
@"TempViewController":@{
            @"TargetSubview" : @"TempView",
            @"TargetSubviewType" : @(1),
            @"TargetEmptyViewType":@"NoDataView"
            },
@"TempWebviewController":@{
            @"TargetSubview" : @"WKWebView",
            @"TargetSubviewType" : @(2),
            @"TargetEmptyViewType":@"NoDataView"
            }
複製程式碼

然後我們就可以hook UIViewControllerviewDidLoad方法,拿到初始時間,同時開啟一個CADisplayLink定時器進行檢測.

在定時器的方法裡,我們就開始根據需要檢測的頁面,找到目標的子view,然後根據view型別進行相應的判斷即可.如果符合判斷條件,就可以進行上報了.

注意點:

1.對於空白頁的處理,需要考慮多種情況,例如是直接加在關鍵子view裡還是加在控制器中.
2.啟動的廣告頁是否對首頁載入有影響.
3.這裡遍歷子控制元件的時候,注意子控制元件層次不能太深,最好是一層,不然可能超過16.7ms,造成誤報,這種情況是一個比較蛋疼的點,需要我們去控制子view的層級,但是為了更精準的獲得載入時間, 這一步也很值得,這也是說為啥要用後臺介面去控制,就是為了在業務發生變化之後能靈活調整view的層級.

以上就是我的檢測思路,並且專案中已經執行了幾個版本,中間也發現了不少問題,並得以解決. 這裡有一個比較簡單的demo,可以讓大家瞭解一下,有啥問題,歡迎指正!

相關文章