【iOS性能監(jiān)控】- FPS

本篇文章主要包含以下方面
1、如何獲取FPS數(shù)據(jù)
2、如何處理數(shù)據(jù)
3、上傳策略

如何獲取FPS數(shù)據(jù)

創(chuàng)建CADisplayLink,設(shè)置CADisplayLink的target的時候,用中間代理類做一次轉(zhuǎn)發(fā),代理類內(nèi)部弱引用當前self,防止產(chǎn)生循環(huán)強引用。設(shè)置每秒的刷新幀率為60次,由于高刷屏的出現(xiàn),這里可能需要做些微調(diào)。

- (CADisplayLink *)displayLink
{
    // Lazily create the display link.
    if (_displayLink == nil)
    {
        _displayLink = [CADisplayLink displayLinkWithTarget:[MMAPMWeakProxy proxyWithTarget:self] selector:@selector(updateFPSAction:)];
        _displayLink.preferredFramesPerSecond = 60;
    }
    return _displayLink;
}

添加到子線程RunLoop中,需要注意的是子線程要做?;?,這里不再贅述具體怎么做

- (void)start
{
    if (_displayLink != nil)
    {
        return;
    }
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

displayLink的回調(diào),更新計數(shù)count,超過1秒采樣一次,計算間隔時間內(nèi)的刷新頻率,fps就是我們需要的數(shù)據(jù)

- (void)updateFPSAction:(CADisplayLink *)displayLink
{
    if (self.lastUpdateTime == 0)
    {
        self.lastUpdateTime = displayLink.timestamp;
    }
    ++self.count;
    NSTimeInterval interval = displayLink.timestamp - self.lastUpdateTime;
    if (interval < self.updateFPSInterval)
    {
        return;
    }
    self.lastUpdateTime = displayLink.timestamp;
    self.fps = self.count/interval;
    self.count = 0;
}

如何處理數(shù)據(jù)

首先思考一個問題,做FPS監(jiān)控的目的是什么?

  1. 獲得app的FPS指標
  2. 優(yōu)化低刷新率的頁面,獲得更好的用戶體驗

所以FPS需要按用戶進入的頁面進行分組,在進入頁面的時候調(diào)用

- (void)setVCPageName:(NSString *)vcName pageType:(NSString *)pageType
{
    @synchronized (self) {
        self.vcName = [MMAPMUtil shouldRecordForVCPageName:vcName] == YES ? vcName : nil;
    }
}

創(chuàng)建GCDTimer,每秒獲取一次fps數(shù)據(jù)。

  • 如果fps幀率小于設(shè)定的閾值立馬上報,這里的閾值默認是50,可以配制成服務(wù)端獲取變量
  • 如果沒有小于最小fps值,則每隔 sysLogTriggerInterval 時間取平均數(shù)上報,這里設(shè)置為1分鐘
- (void)addTimer
{
    [self removeTimer];
    self.GCDTimer = [[AndyGCDTimer alloc] initInQueue:[AndyGCDQueue globalQueue]];
    __weak typeof(self) weakSelf = self;
    [self.GCDTimer timerExecute:^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf.vcName.length == 0) return;
        
        NSUInteger fps = [strongSelf.fpsMonitor getFPS];
        // 如果 當前fps 小于 最小設(shè)定值,則立刻觸發(fā)上報
        if (fps <= [MMAPMConfig sharedConfig].FPSPerfMinValue)
        {
            // 觸發(fā)上報就拿當前的 fps記錄到 log v2
            @synchronized (strongSelf) {
                // 首先從緩存中移除記錄
                if ( !MM_IS_STR_NIL(strongSelf.vcName) ) {
                    [strongSelf.pageSysDictM removeObjectForKey:strongSelf.vcName];
                }
            }
            // 立刻上報當前頁面的sys信息
            [strongSelf recordVCName:strongSelf.vcName fps:fps];
        }
        else
        {
            // 不斷組合頁面sys信息
            [strongSelf combineSysFPS:fps];
            
            // 如果沒有小于最小fps值,則每隔 sysLogTriggerInterval 時間取平均數(shù)上報
            NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
            if (strongSelf->_lastLogTimestamp + 60 < now)
            {
                // 組合fps信息寫入log v2 日志系統(tǒng)
                [strongSelf recordLog];
            }
        }
        
    } timeIntervalWithSecs:1];
    
    [self.GCDTimer resume];
}

按頁面分組,把fps數(shù)據(jù)添加到數(shù)組中

- (void)combineSysFPS:(NSUInteger)fps
{
    @synchronized (self) {
        if (self.vcName.length == 0) return;
        
        NSMutableDictionary *recordDictM = self.pageSysDictM[self.vcName];
        if (recordDictM == nil)
        {
            recordDictM = [NSMutableDictionary dictionary];
        }
        
        NSMutableArray *fpsArrM = recordDictM[FPS_KEY];
        if (fpsArrM == nil)
        {
            fpsArrM = [NSMutableArray array];
            recordDictM[FPS_KEY] = fpsArrM;
        }
        [fpsArrM addObject:@(fps)];
      
        [self.pageSysDictM setValue:recordDictM forKey:self.vcName];
    }
}

上傳策略

計算每個頁面的fps平均值

- (void)recordLog
{
    // 如果當前觸發(fā)日志記錄,則及時記錄更新當前l(fā)og的時間
    NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
    _lastLogTimestamp = now;
    
    @synchronized (self) {
        [self.pageSysDictM enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull vcName_Key, NSMutableDictionary * _Nonnull recordDict, BOOL * _Nonnull stop) {
            __block NSUInteger fps_avg;
            [recordDict enumerateKeysAndObjectsUsingBlock:^(NSString *  _Nonnull key, NSArray *  _Nonnull arr, BOOL * _Nonnull stop) {
                if ([key isEqualToString:FPS_KEY])
                {
                    fps_avg = @(round(1.0 * [[arr valueForKeyPath:@"@sum.unsignedIntegerValue"] unsignedIntegerValue] / arr.count)).unsignedIntegerValue;
                }
            }];
            
            [self recordVCName:vcName_Key fps:fps_avg ];
        }];
        
        [self.pageSysDictM removeAllObjects];
    }
}

上傳到服務(wù)器

- (void)recordVCName:(NSString *)vcName fps:(NSUInteger)fps
{
    NSString *pageType = nil;
    @synchronized (self) {
        pageType = self.vc2PageTypeDictM[vcName];
    }
    if (vcName.length == 0 || pageType.length == 0 || [vcName hasPrefix:@"/"]) return;
    NSString *fpsStr = @(fps).stringValue;
 
 
    //記錄到log v2 日志庫
    NSDictionary *dict = @{@"fps" : fpsStr};
    [MMAPMLogger trackEvent:Track_Event_FPS_Monitor eventParams:@{@"page_name" : mm_safes(vcName), @"page_type":mm_safes(pageType), @"sys_data" : dict}];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容