iOS MetricsKit 收集电量和性能数据

App 的耗电量和性能是用户体验的重要部分,在 iOS 13 中推出了MetricKit,它用于收集和处理电池和性能指标。Improving Battery Life and Performance 中提出了三个用于不同阶段的工具进行电量和性能数据的收集,便于发现各个阶段出现的电量和性能问题。

  • XCTest Metrics (开发和测试阶段)
    • 可以使用 blocks 测试性能
  • MetricsKit (Beta 和 Public Release 阶段)
    • 用于收集电量和性能数据
  • Xcode Metrics Organizer (Public Release 阶段)
    • Xcode 中汇总了,电池,性能和 I/O 的指标

重要的两个分类指标

  • Battery
  • Performance

耗电的体现在下面这几种情况:

  • Processing (CPU 和 GPU 耗时时间)
  • Location (累计使用时长和后台使用时长)
  • Display (平均像素亮度)
  • Networking (上传和下载字节,连接性)
  • Accessories(外围设备:蓝牙等)
  • Multimedia(多媒体)
  • Camera(相机)

性能的体现在下面这几种情况:

  • Hangs (主线程卡顿)
  • Disk (I/O)
  • Application Launch (启动时间)
  • Memory (内存)

开发和测试阶段

可以通过下方代码来测试一些操作的指标

- (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.实现指标数据回调
- (void)didReceiveMetricPayloads:(NSArray *)payloads {
    [payloads enumerateObjectsUsingBlock:^(MXMetricPayload * _Nonnull payload, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@", payload.DictionaryRepresentation);
    }];
}

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

@end

回调时机是,当 App 累计运行了 24 小时 就进行回调

回调数据格式

{
    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";
}

自定义打点, 收集某个范围内的指标数据

__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
  • 数据收集满足用户隐私

Xcode Metrics Organizer 如何运作

当使用 App 时候,iOS 会记录各项指标,然后发送到苹果服务端上,并自动生成相关的可视化报告。

可以通过 Window -> Organizer -> Metrics 查看,可以查看各项指标,并且和历史版本进行对比。

  • 内存提供了峰值和平均内存
  • 电量有前后台占用情况,CPU 、定位和网络的比重等。
  • 历史版本启动耗时
  • 主线程卡顿情况
  • 写磁盘数据量

你可能感兴趣的:(iOS MetricsKit 收集电量和性能数据)