内容简介:在一次组内会议中,被分配到了这样一个技术研究需求,目的是通过检测页面加载耗时,来对页面进行针对性的优化.拿到这个任务之后,立马去搜集了一些网上现有的资料,并作出了一些总结.通常是利用
在一次组内会议中,被分配到了这样一个技术研究需求,目的是通过检测页面加载耗时,来对页面进行针对性的优化.拿到这个任务之后,立马去搜集了一些网上现有的资料,并作出了一些总结.
目前实现检测的几种方式
基本思路
通常是利用 swizlling
在 viewDidLoad
方法里保存一个初始时间,然后在 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
出现,就可以确定这个时间.那我们怎么判断这些子页面真的显示呢?
visibleCells
知道如何判断页面显示了,那么我们需要一个配置文件,来指定那些页面 关键子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 UIViewController
的 viewDidLoad
方法,拿到初始时间,同时开启一个 CADisplayLink
定时器进行检测.
在定时器的方法里,我们就开始根据需要检测的页面,找到目标的子view,然后根据view类型进行相应的判断即可.如果符合判断条件,就可以进行上报了.
注意点:
1.对于空白页的处理,需要考虑多种情况,例如是直接加在关键子view里还是加在控制器中.
2.启动的广告页是否对首页加载有影响.
3.这里遍历子控件的时候,注意子控件层次不能太深,最好是一层,不然可能超过16.7ms,造成误报,这种情况是一个比较蛋疼的点,需要我们去控制子view的层级,但是为了更精准的获得加载时间, 这一步也很值得,这也是说为啥要用后台接口去控制,就是为了在业务发生变化之后能灵活调整view的层级.
以上就是我的检测思路,并且项目中已经运行了几个版本,中间也发现了不少问题,并得以解决. 这里有一个 比较简单的demo ,可以让大家了解一下,有啥问题,欢迎指正!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 攻击者通过 Windows 自带工具加载挖矿程序的检测分析
- 介绍同步加载、异步加载、延迟加载[原创]
- .net加载失败的程序集重新加载
- 虚拟机类加载机制:类加载时机
- 探秘类加载器和类加载机制
- hibernate中加载策略+批加载+懒加载异常【原创】
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。