MetriKit是iOS 13中用於收集和處理電池和性能指標的新框架。這是在WWDC今年與XCTestMetrics和Xcode Metrics組織者一起,作為一項協調一致的努力的一部分,為開發人員帶來關於他們的應用程式在該領域的表現的新見解。 蘋果會自動從AppStore上安裝的應用程式中收集度量 ...
MetriKit是iOS 13中用於收集和處理電池和性能指標的新框架。這是在WWDC今年與XCTestMetrics和Xcode Metrics組織者一起,作為一項協調一致的努力的一部分,為開發人員帶來關於他們的應用程式在該領域的表現的新見解。
蘋果會自動從AppStore上安裝的應用程式中收集度量指標。您可以在Xcode 11中通過打開組織者(⌥ ⌘ ⇧ o)並選擇新的Metrics選項卡。
MetriKit是Xcode組織者度量的補充,它提供了一種編程方式來接收有關應用程式在該領域中的表現的日常信息。有了這些信息,您可以自己收集、聚合和分析比通過Xcode更詳細的信息。
文章結尾有禮包
理解應用度量
度量可以幫助您發現您在本地測試時可能沒有看到的問題,並允許您跟蹤和更改不同版本的應用程式。在這個最初的版本中,蘋果專註於兩個對用戶最重要的指標:電池使用和性能.
電池使用
電池壽命取決於許多不同的因素。物理方面,如設備的年齡和充電周期的次數是決定性的,
但你的手機使用方式也很重要。
比如CPU的使用、顯示器的亮度和屏幕上的顏色,以及收音機用於獲取數據或獲取當前位置的頻率--所有這些都會產生很大的影響。但最重要的是要記住的是,用戶非常關心電池的使用壽命。
除了相機有多好外,電荷之間的間隔時間也是很長的。這個這些天當有人買新手機時的決定因素。
所以當他們的新的,昂貴的手機不度過這一天,他們會很不開心的。
直到最近,蘋果公司還在電池問題上承擔了大部分的責任。
但自從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/