“Hope for the best. ”
一,为什么要研究APP性能
随着移动互联网向纵深发展,用户变得越来越关心应用的体验,开发者必须关注应用性能所带来的用户流失问题。
据统计,有十种应用性能问题危害最大,分别为:连接超时、闪退、卡顿、崩溃、黑白屏、网络劫持、交互性能差、CPU 使用率问题、内存泄露、不良接口。
开发者难以兼顾所有的性能问题,而在传统的开发流程中,我们解决性能问题的方式通常是在得到线上用户的反馈后,再由开发人员去分析引发问题的根源。
显然,凭借用户的反馈来得知应用的性能问题这种方式很原始,也很不高效,它使得开发团队在应对应用性能问题上很被动;所以寻找一种更专业和高效的手段来保障应用的性能就变得势在必行。性能数据获取,展示在GT HTML页面上,能帮助开发团队快速精确地定位性能问题,进而推动应用的性能和用户体验的提升。
二,iOS性能数据指标
iOS主要性能指标 CPU 占用率、内存使用情况、FPS、冷启动、热启动时间,耗电量基础性能指标
页面渲染时间,APP页面卡顿,Crash堆栈信息,APP内存泄漏数据,APP消耗用户流量等一些主要指标
三,iOS性能数据获取
获取性能数据是多个类来获取的,每一个类获取数据指标都有不同
获取性能数据类目录:
这个是pod封装整体,如果要外面接入性能数据,直接在Podfile里写入pod ‘HLAppMonitor’,再pod下来,在APPdelegate中 didFinishLaunchingWithOptions方法中写入
//开启性能数据测试 ,默认频率,一分钟上传给沙盒 1s收集基本性能数据测试
TDPerformanceDataManager.sharedInstance().startToCollectPerformanceData()
开启性能数据监控收集,默认每隔1s秒中获取基本性能数据,每隔1分钟种时间写入沙盒里txt文件中,可以注意的是:收集性能数据和写入数据都是子线程异步操作的(TDDispatchAsync类),不会影响APP本身,这个等下下面会说明的
性能数据获取是一个单例[TDPerformanceDataManager sharedInstance]来对性能数据处理收集的
/**
定时将数据字符串写入沙盒文件
@param intervaTime 上传文件时间间隔,basicTime 基本性能数据获取间隔时间
*/
- (void)startRecordDataIntervalTime: (NSInteger)intervaTime withBasicTime:(NSInteger)basicTime {
//如果有监控 就不用往下走了
if (self.isMonitoring) {//如果正在监控 就不用了
return;
}
self.isMonitoring = YES;
//开启监控
if (!self.isStartMonitor) {//是否开始监控
return;
}
//记录基本性能数据定时器间隔
if (basicTime > 0) {
self.basicTime = basicTime;
}else{
self.basicTime = 1;
}
//记录写入沙盒数据定时器时间间隔
if (intervaTime > 0) {
self.intervaTime = intervaTime;
}else{
self.intervaTime = 60;
}
//开始缓存机制
self.isStartCasch = YES;
//记录开始时间
self ->startTime = [self currentTime];
//获取APP基本性能数据
[self getAppBaseInfo];
//开启基本性能数据 //基本性能数据获取定时器
self.isStartBaseMonitor = YES;
//开启帧率FPS监控
self.isStartFPSMonitor = YES;
//网络 开启网络流量监控
self.isStartNetworkMonitor = YES;
//卡顿//开启anrEye
self.isStartCatonMonitor = YES;
//崩溃开启奔溃检测
self.isStartCrashMonitor = YES;
if (td_resource_recordDataIntervalTime_callback_key != nil) {return;}
//设置定时器间隔
[TDGlobalTimer setUploadCallbackInterval:self.intervaTime];
//监听数据
__weak typeof(self) weakSelf = self;
td_resource_recordDataIntervalTime_callback_key = [[TDGlobalTimer uploadRegisterTimerCallback: ^{
dispatch_async(td_log_IO_queue(), ^{
//将String写入文件
//结束时间
long long curt = [self currentTime];
NSString *currntime = [NSString stringWithFormat:@"%lld",curt];
[weakSelf getStringResourceDataTime:currntime withStartOrEndTime:currntime withIsStartTime:NO];
NSData *normalData = [weakSelf.normalDataStr dataUsingEncoding:NSUTF8StringEncoding];
[weakSelf writeToFileWith:normalData];
});
}] copy];
}
这次改版有重要变化就是每一个性能指标都可以单独监控器指标(分为总开关,基本性能数据,FPS获取,网络数据监控,卡顿信息监控,崩溃信息监控)
以下是性能指标开关
//开启监控总开关
- (void)setIsStartMonitor:(BOOL)isStartMonitor {
_isStartMonitor = isStartMonitor;
if (isStartMonitor) {
[self startRecordDataIntervalTime:self.intervaTime withBasicTime:self.basicTime];
}else{
[self stopAppPerformanceMonitor];
}
}
//基本性能数据
- (void)setIsStartBaseMonitor:(BOOL)isStartBaseMonitor {
_isStartBaseMonitor = isStartBaseMonitor;
if (isStartBaseMonitor) {
if (self.basicTime > 0) {
[self startBasicResourceDataTime: self.basicTime];
}else{
[self startBasicResourceDataTime: 1];
}
}else{
[self stopResourceData];
}
}
//帧率fps
- (void)setIsStartFPSMonitor:(BOOL)isStartFPSMonitor {
_isStartFPSMonitor = isStartFPSMonitor;
if (isStartFPSMonitor) {
//开启fps监控
[[TDFPSMonitor sharedMonitor]startMonitoring];
//开启fps检测
[TDFPSMonitor sharedMonitor].delegate = self;
}else{
//开启fps监控
[[TDFPSMonitor sharedMonitor]stopMonitoring];
//开启fps检测
[TDFPSMonitor sharedMonitor].delegate = nil;
}
}
//网络
- (void)setIsStartNetworkMonitor:(BOOL)isStartNetworkMonitor {
_isStartNetworkMonitor = isStartNetworkMonitor;
if (isStartNetworkMonitor) {
//开启网络流量监控
//开启网络监控
[TDNetworkTrafficManager start];
}else{
//暂停网络流量监控
//暂停网络监控
[TDNetworkTrafficManager end];
}
}
//卡顿
- (void)setIsStartCatonMonitor:(BOOL)isStartCatonMonitor {
_isStartCatonMonitor = isStartCatonMonitor;
if (isStartCatonMonitor) {
if (self->anrEye == nil) {
self->anrEye = [[ANREye alloc] init];
}
//开启anrEye
self->anrEye.delegate = self;
[self->anrEye openWith:1];
}else{
if (self ->anrEye) {
self->anrEye.delegate = nil;
[self->anrEye close];
}
}
}
//崩溃
- (void)setIsStartCrashMonitor:(BOOL)isStartCrashMonitor {
_isStartCrashMonitor = isStartCrashMonitor;
if (isStartCrashMonitor) {
//开启奔溃检测
[CrashEye addWithDelegate:self];
}else{
//移除奔溃检测
[CrashEye removeWithDelegate:self];
}
}
外界可以通过统一方法来控制监控开关
//改变监控指标状态 Indicators:监控指标,isStartMonitor:监控是否开启与关闭
- (void)didChangeMonitoringIndicators: (TDMonitoringIndicators)Indicators withChangeStatus:(BOOL)isStartMonitor {
switch (Indicators) {
case _TDMonitoringIndicatorsALL://所有的
self.isStartMonitor = isStartMonitor;
break;
case _TDMonitoringIndicatorsBase://基本性能数据
self.isStartBaseMonitor = isStartMonitor;
break;
case _TDMonitoringIndicatorsFPS://帧率FPS
self.isStartFPSMonitor = isStartMonitor;
break;
case _TDMonitoringIndicatorsNetwork://网络
self.isStartNetworkMonitor = isStartMonitor;
break;
case _TDMonitoringIndicatorsCaton://卡顿
self.isStartCatonMonitor = isStartMonitor;
break;
case _TDMonitoringIndicatorsCrash://崩溃
self.isStartCrashMonitor = isStartMonitor;
break;
default:
break;
}
}
—— Marco 后记于 2018.07