一. 背景
我們從啟動統(tǒng)計數(shù)據(jù)上發(fā)現(xiàn)耐亏,在iOS15
及以上相關(guān)系統(tǒng)搂誉,經(jīng)常會出現(xiàn)啟動耗時幾十秒的情況莲蜘,通常是load
方法的加載就已經(jīng)達(dá)到幾十秒了洗出,而且都是相對性能較好的機型士复。
由于蘋果系統(tǒng)存在watchdog
機制,針對App啟動的啟動翩活,watchdog
的時間限制是20s
阱洪,如果啟動超過20s
,就會被watchdog
強制殺死進(jìn)程菠镇,退出應(yīng)用冗荸。
但這里統(tǒng)計出來啟動時長,很明顯很大部分已經(jīng)超過20s
了利耍,但是依然正常啟動蚌本,說明要不就啟動統(tǒng)計方法有問題,要不就存在一些異常情況隘梨,出現(xiàn)數(shù)據(jù)異常程癌。
二. 分析
針對出現(xiàn)的機型主要是在iOS15
及以上系統(tǒng),因此依據(jù)這個線索進(jìn)行排查轴猎。終于找到了關(guān)于prewarm
的相關(guān)資料嵌莉,具體詳見: 【About the app launch sequence】
1. prewarm 機制
Apple
在iOS 15
中引入了prewarm
(預(yù)熱)機制,系統(tǒng)可能會根據(jù)設(shè)備的情況捻脖,比如當(dāng)設(shè)備內(nèi)存和磁盤空間充足以及用戶啟動該App
的時間習(xí)慣等锐峭,提前幫你預(yù)熱(prewarm) 你的 App
。
也就是提前啟動不再運行的 App 進(jìn)程可婶,以減少用戶手動啟動 App
等待的時間沿癞。prewarm
執(zhí)行一個 App
的啟動序列直到(但不包括)當(dāng) main()
調(diào)用 UIApplicationMain
。也就是prewarm
幫忙提前完成了pre-main
之前的所有操作扰肌。
這為系統(tǒng)提供了一個機會來構(gòu)建和緩存它需要的任何低層結(jié)構(gòu)抛寝,以期待一個完整的啟動。也就是說曙旭,prewarm
機制可以減少啟動時間盗舰,我們甚至可以在 load
方法中做一些資源的預(yù)加載。
2. 原因
因為iOS15
及以后的系統(tǒng)桂躏,蘋果添加這種預(yù)熱的操作:prewarm
钻趋,系統(tǒng)提前執(zhí)行了啟動的pre-main
之前的所有操作,然后暫停剂习,等到用戶真正啟動的時候蛮位,才會繼續(xù)執(zhí)行mian
之后的相關(guān)啟動操作较沪,而我們App
啟動耗時統(tǒng)計是從cocoapods
排序的第一個庫的load
時間開始,然后到首頁的viewDidAppear
結(jié)束失仁。
因此如果是該App
被預(yù)熱了尸曼,也就是庫的load
等方法先執(zhí)行,這時候統(tǒng)計起始時間已經(jīng)標(biāo)記萄焦,然后暫停控轿,暫停這段時間也被算進(jìn)去,導(dǎo)致啟動耗時尤其是load
方法的加載出現(xiàn)幾十秒這種情況拂封,統(tǒng)計數(shù)據(jù)出現(xiàn)了偏差茬射,之所以只有統(tǒng)計到幾十秒的,而沒有幾分鐘或者幾個小時冒签,是因為統(tǒng)計庫做了過濾在抛,超過1
分鐘,就直接丟棄這次統(tǒng)計萧恕。
三. 解決方法
我們可以根據(jù)如下方法判斷此次啟動是否為預(yù)熱prewarm
啟動:
func isPrewarmLaunch() -> Bool {
let systemVersion = UIDevice.current.systemVersion.toFloat() ?? 0.0
if systemVersion >= 15.0 {
let environment = ProcessInfo.processInfo.environment
for key in environment.allKeys() {
if key.contains(substring: "prewarm") {
return true
}
}
}
return false
}
因此決定啟動組件里面添加判斷刚梭,判斷此次啟動是否為預(yù)熱prewarm
啟動,如果此次啟動為預(yù)熱prewarm
啟動廊鸥,則統(tǒng)計的起始點用main
函數(shù)的開始執(zhí)行的時間作為起點望浩,如果不是預(yù)熱啟動,則依然使用cocoapods
排序的第一個庫的load
的加載時間作為起點惰说。
不過這里需要對你的App
是否接入UIScene
做區(qū)分判斷:
對于接入 UIScene
的 App
:
application:didFinishLaunchingWithOptions:
可能會被調(diào)用(并不總是發(fā)生)scene:willConnectToSession:options:
未被調(diào)用磨德。事實上,SceneDelegate
直到App
打開才創(chuàng)建吆视。
對于沒有接入 UIScene
的 App
:
-
application:didFinishLaunchingWithOptions:
不會調(diào)用典挑。
因此,如果 App
接入了 UIScene
啦吧,又沒做特殊處理的話您觉,會因為 prewarm
機制的 bug
而導(dǎo)致 iOS 15
用戶日活異常增多。
因為我們業(yè)務(wù)線相關(guān)的App
沒有接入UIScene
授滓,因此我們這里直接采取上面的優(yōu)化方案琳水。