iOS MetricsKit 收集電量和性能數(shù)據(jù)

App 的耗電量和性能是用戶體驗的重要部分,在 iOS 13 中推出了MetricKit,它用于收集和處理電池和性能指標。Improving Battery Life and Performance 中提出了三個用于不同階段的工具進行電量和性能數(shù)據(jù)的收集,便于發(fā)現(xiàn)各個階段出現(xiàn)的電量和性能問題。

  • XCTest Metrics (開發(fā)和測試階段)
    • 可以使用 blocks 測試性能
  • MetricsKit (Beta 和 Public Release 階段)
    • 用于收集電量和性能數(shù)據(jù)
  • Xcode Metrics Organizer (Public Release 階段)
    • Xcode 中匯總了,電池,性能和 I/O 的指標

重要的兩個分類指標

  • Battery
  • Performance

耗電的體現(xiàn)在下面這幾種情況:

  • Processing (CPU 和 GPU 耗時時間)
  • Location (累計使用時長和后臺使用時長)
  • Display (平均像素亮度)
  • Networking (上傳和下載字節(jié),連接性)
  • Accessories(外圍設備:藍牙等)
  • Multimedia(多媒體)
  • Camera(相機)

性能的體現(xiàn)在下面這幾種情況:

  • Hangs (主線程卡頓)
  • Disk (I/O)
  • Application Launch (啟動時間)
  • Memory (內(nèi)存)

開發(fā)和測試階段

可以通過下方代碼來測試一些操作的指標

- (void)testReSizeImagePerformance {
    __auto_type app = [XCUIApplication new];
    [self
        measureWithMetrics:@[
            XCTMemoryMetric.new,
            XCTClockMetric.new,
            XCTCPUMetric.new
        ]
        block:^{
            [app.buttons[@"reSize"] tap];
        }];
}

啟動時間

- (void)testLaunchPerformance {
    if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
        [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{
            [[[XCUIApplication alloc] init] launch];
        }];
    }
}

MetricsKit

#import "AppMetricManagerSubscriber.h"
@import MetricKit;

@interface AppMetricManagerSubscriber()
<
MXMetricManagerSubscriber
>

@end

@implementation AppMetricManagerSubscriber

- (instancetype)init {
    self = [super init];
    if (!self) { return nil; }
    // 1.添加訂閱
    [MXMetricManager.sharedManager addSubscriber:self];
    return self;
}

// 2.實現(xiàn)指標數(shù)據(jù)回調(diào)
- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload *> *)payloads {
    [payloads enumerateObjectsUsingBlock:^(MXMetricPayload * _Nonnull payload, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@", payload.DictionaryRepresentation);
    }];
}

- (void)dealloc {
    // 3.移除訂閱
    [MXMetricManager.sharedManager removeSubscriber:self];
}

@end

回調(diào)時機是,當 App 累計運行了 24 小時 就進行回調(diào)

回調(diào)數(shù)據(jù)格式

{
    appVersion = "1.0";
    applicationLaunchMetrics =     {
        histogrammedResumeTime =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 60;
                    bucketEnd = "210 ms";
                    bucketStart = "200 ms";
                };
                1 =                 {
                    bucketCount = 70;
                    bucketEnd = "310 ms";
                    bucketStart = "300 ms";
                };
                2 =                 {
                    bucketCount = 80;
                    bucketEnd = "510 ms";
                    bucketStart = "500 ms";
                };
            };
        };
        histogrammedTimeToFirstDrawKey =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 50;
                    bucketEnd = "1,010 ms";
                    bucketStart = "1,000 ms";
                };
                1 =                 {
                    bucketCount = 60;
                    bucketEnd = "2,010 ms";
                    bucketStart = "2,000 ms";
                };
                2 =                 {
                    bucketCount = 30;
                    bucketEnd = "3,010 ms";
                    bucketStart = "3,000 ms";
                };
            };
        };
    };
    applicationResponsivenessMetrics =     {
        histogrammedAppHangTime =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 50;
                    bucketEnd = "100 ms";
                    bucketStart = "0 ms";
                };
                1 =                 {
                    bucketCount = 60;
                    bucketEnd = "400 ms";
                    bucketStart = "100 ms";
                };
                2 =                 {
                    bucketCount = 30;
                    bucketEnd = "700 ms";
                    bucketStart = "400 ms";
                };
            };
        };
    };
    applicationTimeMetrics =     {
        cumulativeBackgroundAudioTime = "30 sec";
        cumulativeBackgroundLocationTime = "30 sec";
        cumulativeBackgroundTime = "40 sec";
        cumulativeForegroundTime = "700 sec";
    };
    cellularConditionMetrics =     {
        cellConditionTime =         {
            histogramNumBuckets = 3;
            histogramValue =             {
                0 =                 {
                    bucketCount = 20;
                    bucketEnd = "1 bars";
                    bucketStart = "1 bars";
                };
                1 =                 {
                    bucketCount = 30;
                    bucketEnd = "2 bars";
                    bucketStart = "2 bars";
                };
                2 =                 {
                    bucketCount = 50;
                    bucketEnd = "3 bars";
                    bucketStart = "3 bars";
                };
            };
        };
    };
    cpuMetrics =     {
        cumulativeCPUTime = "100 sec";
    };
    diskIOMetrics =     {
        cumulativeLogicalWrites = "1,300 kB";
    };
    displayMetrics =     {
        averagePixelLuminance =         {
            averageValue = "50 apl";
            sampleCount = 500;
            standardDeviation = 0;
        };
    };
    gpuMetrics =     {
        cumulativeGPUTime = "20 sec";
    };
    locationActivityMetrics =     {
        cumulativeBestAccuracyForNavigationTime = "20 sec";
        cumulativeBestAccuracyTime = "30 sec";
        cumulativeHundredMetersAccuracyTime = "30 sec";
        cumulativeKilometerAccuracyTime = "20 sec";
        cumulativeNearestTenMetersAccuracyTime = "30 sec";
        cumulativeThreeKilometersAccuracyTime = "20 sec";
    };
    memoryMetrics =     {
        averageSuspendedMemory =         {
            averageValue = "100,000 kB";
            sampleCount = 500;
            standardDeviation = 0;
        };
        peakMemoryUsage = "200,000 kB";
    };
    metaData =     {
        appBuildVersion = 1;
        deviceType = "iPhone11,8";
        osVersion = "iPhone OS 13.3 (17C54)";
        regionFormat = CN;
    };
    networkTransferMetrics =     {
        cumulativeCellularDownload = "80,000 kB";
        cumulativeCellularUpload = "70,000 kB";
        cumulativeWifiDownload = "60,000 kB";
        cumulativeWifiUpload = "50,000 kB";
    };
    signpostMetrics =     (
                {
            signpostCategory = TestSignpostCategory1;
            signpostIntervalData =             {
                histogrammedSignpostDurations =                 {
                    histogramNumBuckets = 3;
                    histogramValue =                     {
                        0 =                         {
                            bucketCount = 50;
                            bucketEnd = "100 ms";
                            bucketStart = "0 ms";
                        };
                        1 =                         {
                            bucketCount = 60;
                            bucketEnd = "400 ms";
                            bucketStart = "100 ms";
                        };
                        2 =                         {
                            bucketCount = 30;
                            bucketEnd = "700 ms";
                            bucketStart = "400 ms";
                        };
                    };
                };
                signpostAverageMemory = "100,000 kB";
                signpostCumulativeCPUTime = "30,000 ms";
                signpostCumulativeLogicalWrites = "600 kB";
            };
            signpostName = TestSignpostName1;
            totalSignpostCount = 30;
        },
                {
            signpostCategory = TestSignpostCategory2;
            signpostIntervalData =             {
                histogrammedSignpostDurations =                 {
                    histogramNumBuckets = 3;
                    histogramValue =                     {
                        0 =                         {
                            bucketCount = 60;
                            bucketEnd = "200 ms";
                            bucketStart = "0 ms";
                        };
                        1 =                         {
                            bucketCount = 70;
                            bucketEnd = "300 ms";
                            bucketStart = "201 ms";
                        };
                        2 =                         {
                            bucketCount = 80;
                            bucketEnd = "500 ms";
                            bucketStart = "301 ms";
                        };
                    };
                };
                signpostAverageMemory = "60,000 kB";
                signpostCumulativeCPUTime = "50,000 ms";
                signpostCumulativeLogicalWrites = "700 kB";
            };
            signpostName = TestSignpostName2;
            totalSignpostCount = 40;
        }
    );
    timeStampBegin = "2020-02-08 16:00:00 +0000";
    timeStampEnd = "2020-02-09 15:59:00 +0000";
}

自定義打點, 收集某個范圍內(nèi)的指標數(shù)據(jù)

__auto_type reSizeLog = [MXMetricManager makeLogHandleWithCategory:@"Image"];
os_signpost_id_t signpost_id = os_signpost_id_generate(reSizeLog);
MXSignpostIntervalBegin(reSizeLog, signpost_id, "reSizeImage");
[self reSizeImage];
MXSignpostIntervalEnd(reSizeLog, signpost_id, "reSizeImage");

Xcode Metrics Organizer

什么是 Xcode Metrics Organizer?

  • 開箱即用的電量和性能分析工具
  • 無須改動 App
  • 數(shù)據(jù)收集滿足用戶隱私

Xcode Metrics Organizer 如何運作

當使用 App 時候,iOS 會記錄各項指標,然后發(fā)送到蘋果服務端上,并自動生成相關的可視化報告。

可以通過 Window -> Organizer -> Metrics 查看,可以查看各項指標,并且和歷史版本進行對比。

  • 內(nèi)存提供了峰值和平均內(nèi)存
  • 電量有前后臺占用情況,CPU 、定位和網(wǎng)絡的比重等。
  • 歷史版本啟動耗時
  • 主線程卡頓情況
  • 寫磁盤數(shù)據(jù)量
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 本文由作者張迎貞授權(quán)網(wǎng)易云社區(qū)發(fā)布。 APP性能測試除了需要監(jiān)控PCU、內(nèi)存占用、流量等,還需要獲取APP的電量數(shù)...
    43ce3d72fadb閱讀 826評論 0 0
  • 北京歡迎你,為你開天辟地,流動中的魅力,充滿著朝氣。 ――《北京歡迎你》 ...
    古上心月閱讀 149評論 0 0
  • 七月與安生寫影評 看完七月與安生感嘆世間竟有這么好的感情,這么多年,經(jīng)歷了那么多事,希望他們像小說中活成對方的樣子...
    那年冬天小雪閱讀 216評論 0 0
  • Xcode常用插件 [http://www.cnblogs.com/dsxniubility/p/5099191....
    騂躍神話閱讀 326評論 0 0
  • 茫白的天,孤獨的人,和他自己的熱鬧。 他生在潺潺水聲中也活在人聲鼎沸里。 他生在江南煙雨中也活在無疆大漠里。 他看...
    柳搖新綠閱讀 344評論 4 11