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ù)量