iOS開發高級分享 - iOS 13 中的新框架 — MetriKit

MetriKit是iOS 13中用於收集和處理電池和性能指標的新框架。這是在WWDC今年與XCTestMetrics和Xcode Metrics組織者一塊兒,做爲一項協調一致的努力的一部分,爲開發人員帶來關於他們的應用程序在該領域的表現的新看法。web

WWDC 2019會話417的圖表:「提升電池壽命和性能

蘋果會自動從AppStore上安裝的應用程序中收集度量指標。您能夠在Xcode 11中經過打開組織者(⌥ ⌘ ⇧ o)並選擇新的Metrics選項卡。sql

MetriKit是Xcode組織者度量的補充,它提供了一種編程方式來接收有關應用程序在該領域中的表現的平常信息。有了這些信息,您能夠本身收集、聚合和分析比經過Xcode更詳細的信息。數據庫

文章結尾有禮包

 理解應用度量

度量能夠幫助您發現您在本地測試時可能沒有看到的問題,並容許您跟蹤和更改不一樣版本的應用程序。在這個最初的版本中,蘋果專一於兩個對用戶最重要的指標:電池使用和性能.編程

電池使用

電池壽命取決於許多不一樣的因素。物理方面,如設備的年齡和充電週期的次數是決定性的,json

但你的手機使用方式也很重要。服務器

好比CPU的使用、顯示器的亮度和屏幕上的顏色,以及收音機用於獲取數據或獲取當前位置的頻率--全部這些都會產生很大的影響。但最重要的是要記住的是,用戶很是關心電池的使用壽命。app

除了相機有多好外,電荷之間的間隔時間也是很長的。這個這些天當有人買新手機時的決定因素。框架

因此當他們的新的,昂貴的手機不度過這一天,他們會很不開心的。函數

直到最近,蘋果公司還在電池問題上承擔了大部分的責任。post

但自從iOS 12和它的新電池使用屏在設置中,用戶如今能夠判斷他們最喜歡的應用程序是何時被指責的。

幸運的是,有了iOS 13,你如今就擁有了全部你須要的東西,以確保你的應用程序不會與合理的能源使用發生衝突。

 

 

 

性能

性能是整個用戶體驗中的另外一個關鍵因素。正常狀況下,咱們可能會看數據,如處理器時鐘速度或幀速率做爲業績的衡量標準。但相反,蘋果專一於不那麼抽象和更具可操做性的指標:

掛率

主/UI線程被阻塞的頻率有多大,以至應用程序對用戶輸入沒有響應?

發射時間

用戶點擊圖標後,應用程序須要多長時間才能使用?

峯值記憶&懸浮記憶

在進入後臺以前,應用程序在峯值時使用了多少內存?

磁盤寫入

這個應用程序多久寫一次到磁盤,若是你還不知道,那就是相對緩慢運行 (即便是iPhone上的閃存!)

使用MetriKit

從API使用者的角度來看,很難想象`MetriKit`如何更容易合併。您所須要的只是您的應用程序的某些部分做爲一個度量訂閱者(一個明顯的選擇是您的`AppDelegate`),並將其添加到共享`MXMetricManager:`

import UIKit
import MetricKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
MXMetricManager.shared.add(self)
return true
}

func applicationWillTerminate(_ application: UIApplication) {
MXMetricManager.shared.remove(self)
}
}

extension AppDelegate: MXMetricManagerSubscriber {
func didReceive(_ payloads: [MXMetricPayload]) {
...
}
}

iOS在使用應用程序時會自動收集樣本,而且天天(每24小時)發送一次包含這些指標的聚合報告。

以驗證您的`MXMetricManagerSubscriber`正在按預期調用其委託方法,在Xcode運行應用程序時,從Debug菜單中選擇SIMPLE MetriKit有效載荷。

模擬MetriKit有效載荷菜單項要求應用程序在實際設備上運行,併爲模擬器構建禁用。

用路標標註關鍵代碼節

除了爲您收集的基線統計數據以外,還可使用`mxSignpost`函數來收集代碼中最重要部分的度量。這,這個路標支持API捕獲CPU時間、內存和寫入磁盤。

例如,若是應用程序的一部分確實對音頻流進行了後處理,您能夠用公制路標對這些區域進行註釋,以肯定該工做的能量和性能影響:

let audioLogHandle = MXMetricManager.makeLogHandle(category: "Audio")

func processAudioStream() {
mxSignpost(.begin, log: audioLogHandle, name: "ProcessAudioStream")
...
mxSignpost(.end, log: audioLogHandle, name: "ProcessAudioStream")
}

建立用於收集AppMetrics的自託管Web服務

既然你掌握了這些信息,你會怎麼處理呢?咱們該怎麼填` ... `實現中的佔位符`didReceive(_:)?`

你,大家能把它轉交給一些付費的分析或事故報告服務,但這其中的樂趣在哪裏呢??讓咱們構建本身的Web服務來收集這些以供進一步分析:

用PostgreSQL存儲和查詢度量

這個 MXMetricPayload 由度量管理器訂閱者接收的對象有一個方便的 jsonRepresentation() 方法生成以下內容:

[點擊查看 JSON表示 ]

正如你所看到的,這個表示法有不少地方。爲全部這些信息定義模式將是一項艱鉅的工做,並且不能保證這種狀況從此不會改變。所以,讓咱們採用NoSQL範式(儘管是負責任地使用波斯特格斯)經過將有效載荷存儲在JSONB列:

CREATE TABLE IF NOT EXISTS metrics (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
payload JSONB NOT NULL
);

咱們可使用JSON算子就像這樣:

SELECT (payload -> 'applicationTimeMetrics'
->> 'cumulativeForegroundTime')::INTERVAL
FROM metrics;
-- interval
-- ═══════════════════
-- @ 11 mins 40 secs
-- (1 row)

 度量的JSON表示將時間和內存的度量存儲爲帶有單位的字符串(如"100 ms"和500 kB)。在Postgres中,您能夠直接將時間度量轉換到INTERVAL類型可是,您須要建立一個轉換爲字節計數的函數:

CREATE OR REPLACE FUNCTION parse_byte_count (TEXT)
RETURNS BIGINT
AS $$
SELECT
replace(split_part($1, ' ', 1),',','')::BIGINT *
CASE split_part($1, ' ', 2)
WHEN 'kB' THEN 1024
WHEN 'MB' THEN 1024 * 1024
WHEN 'GB' THEN 1024 * 1024 * 1024
END
$$ LANGUAGE 'sql' STRICT IMMUTABLE;

 

高級:建立視圖

PostgreSQL中的JSON運算符使用起來可能很麻煩--特別是對於更復雜的查詢。其中一種幫助方法是建立一個視圖。(物化或以其餘方式)要以最方便的表示方式向您投射最重要的信息:

DROP VIEW key_performance_indicators;
CREATE VIEW key_performance_indicators AS
SELECT
id,
(payload -> 'appVersion') AS app_version,
(payload -> 'metaData' ->> 'deviceType') AS device_type,
(payload -> 'metaData' ->> 'regionFormat') AS region,
(payload -> 'applicationTimeMetrics'
->> 'cumulativeForegroundTime'
)::INTERVAL AS cumulative_foreground_time,
parse_byte_count(
payload -> 'memoryMetrics'
->> 'peakMemoryUsage'
) AS peak_memory_usage_bytes
FROM metrics;

使用視圖,您能夠執行聚合查詢在您的全部度量指標中,JSON有效負載都具備模式支持的關係數據庫的方便性:

SELECT avg(cumulative_foreground_time)
FROM key_performance_indicators;
-- avg
-- ══════════════════
-- @ 9 mins 41 secs

SELECT app_version, percentile_disc(0.5)
WITHIN GROUP (ORDER BY peak_memory_usage_bytes)
AS median
FROM key_performance_indicators
GROUP BY app_version;
-- app_version │ median
-- ═════════════╪═══════════
-- "1.0.1"192500000
-- "1.0.0"204800000

PostgreSQL不能很好地處理CamelCase的表名或列名,因此在使用如下函數時要記住這一點jsonb_to_record.

 建立Web服務

在本例中,大多數繁重的工做都委託給Postgres,這使得服務器端的實現至關枯燥。爲了完整起見,如下是Ruby(Sinatra)和JavaScript(Express)中的一些參考實現:

require 'sinatra/base'
require 'pg'
require 'sequel'

class App < Sinatra::Base
configure do
DB = Sequel.connect(ENV['DATABASE_URL'])
end

post '/collect' do
DB[:metrics].insert(payload: request.body.read)
status 204
end
end

 

發送度量做爲JSON

如今咱們已經設置好了一切,最後一步就是實現所需的`MXMetricManagerSubscriber`委託方法`didReceive(_:)`要將這些信息傳遞給咱們的web服務:

extension AppDelegate: MXMetricManagerSubscriber {
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
let url = URL(string: "https://example.com/collect")!

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = payload.jsonRepresentation()

let task = URLSession.shared.dataTask(with: request)
task.priority = URLSessionTask.lowPriority
task.resume()
}
}
}

1024禮包

加入iOS開發交流QQ羣:[1012951431],選擇加入一塊兒交流,一塊兒學習,共享學習資料。期待你的加入!(進羣可領取禮包)

轉載地址 : https://nshipster.com/metrickit/

相關文章
相關標籤/搜索