UI卡頓原理
在 VSync 信號(hào)到來后,系統(tǒng)圖形服務(wù)會(huì)通過 CADisplayLink 等機(jī)制通知 App,App 主線程開始在 CPU 中計(jì)算顯示內(nèi)容,比如視圖的創(chuàng)建、布局計(jì)算、圖片解碼、文本繪制等。隨后 CPU 會(huì)將計(jì)算好的內(nèi)容提交到 GPU 去,由 GPU 進(jìn)行變換、合成、渲染。隨后 GPU 會(huì)把渲染結(jié)果提交到幀緩沖區(qū)去,等待下一次 VSync 信號(hào)到來時(shí)顯示到屏幕上。由于垂直同步的機(jī)制,如果在一個(gè) VSync 時(shí)間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會(huì)被丟棄,等待下一次機(jī)會(huì)再顯示,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變。這就是界面卡頓的原因。
從上面的圖中可以看到,CPU 和 GPU 不論哪個(gè)阻礙了顯示流程,都會(huì)造成掉幀現(xiàn)象。所以開發(fā)時(shí),也需要分別對(duì) CPU 和 GPU 壓力進(jìn)行評(píng)估和優(yōu)化。
卡頓原因分析
1、 通過工具檢查
- 通過Xcode 檢查視圖層級(jí)關(guān)系是否有異常 ------ 未發(fā)現(xiàn)視圖層級(jí)設(shè)置不合理的問題
- 通過instruments 的Animation Hitch工具檢測(cè):------ 碰到問題,錄制完了無數(shù)據(jù)(用iphone7錄制有數(shù)據(jù)的,iphne14錄制無數(shù)據(jù)?),iphone7檢測(cè)結(jié)果發(fā)現(xiàn)掉幀確實(shí)很頻繁
- 用模擬器檢查是否有離屏渲染 ------- 確實(shí)有離屏渲染問題
2、看代碼分析
- 復(fù)用問題
-
- cell上的lottie動(dòng)畫組件沒有復(fù)用;
- cell上有大量的removeFromSuperView導(dǎo)致列表滑動(dòng)過程中頻繁的視圖添加和刪除,同時(shí)本來只需要更新顯示內(nèi)容的控件也沒有得到復(fù)用;
- GPU離屏渲染的問題
-
- 陰影、圓角設(shè)置的方式 ------ 確有圓角設(shè)置導(dǎo)致的離屏渲染
- 光柵化設(shè)置 ------ 無
- 設(shè)置了組透明度為 YES,并且透明度不為 1 的layer ------ 無
- 使用了高斯模糊等 ------ 無
- 渲染耗時(shí)
-
- 圖片體積是否還有壓縮的空間 ------ 商品圖片和店鋪頭像確實(shí)有進(jìn)一步裁減壓縮的空間
- 滑動(dòng)過程中停止lottie動(dòng)畫
- 漸變圖層換成圖片 ------ 減少GPU渲染耗時(shí)
- 檢查是否有線程不合理問題
-
- 主線程耗時(shí)任務(wù),比如調(diào)用UIGraphicsGetCurrentContext等接口在 CPU 上進(jìn)行繪制計(jì)算 ------ 無
- 主線程等待繁忙的子線程或低優(yōu)先級(jí)的后臺(tái)線程任務(wù)而導(dǎo)致阻塞,或者在滑動(dòng)過程中主線程有耗時(shí)方法 ------ 往下滑動(dòng)加載下一頁數(shù)據(jù)時(shí),確實(shí)有耗時(shí)的操作放在主線程
- 主線程等待系統(tǒng)資源,比如使用Data(contentsOf:)進(jìn)行 IO 讀取等 ------ 無
- 內(nèi)存占用過度:列表一直往下刷新內(nèi)存會(huì)爆掉,可能存在內(nèi)存泄漏,需要進(jìn)一步看看
-
- 可以用內(nèi)存工具檢測(cè)看看有沒有異常
- 看代碼里有沒有循環(huán)引用等引起內(nèi)存問題的代碼
- 緩存是否不合理
優(yōu)化步驟和測(cè)試點(diǎn)
第一階段 ------ 10.5.60
優(yōu)化點(diǎn):
- 對(duì)列表Cell的頂部的播放組件重構(gòu)優(yōu)化
-
- 優(yōu)化代碼結(jié)構(gòu);
- 避免在刷新數(shù)據(jù)的時(shí)候添加和刪除界面元素;
- 優(yōu)化左上角lottie動(dòng)畫的加載方式,避免每次刷新數(shù)據(jù)都更新Lottie動(dòng)畫,達(dá)到復(fù)用的目的;
第二階段 ------ 10.5.66
優(yōu)化點(diǎn)
- 雙列Cell底部組件反復(fù)的添加和刪除邏輯優(yōu)化;
- 解決離屏渲染問題;
- 商品圖片和介紹圖預(yù)加載并裁減來減少體積(圖片從100~200Byte,減少到幾個(gè)Byte);
- 解決滑動(dòng)過程中停止播放器定時(shí)器生效導(dǎo)致播放器一直在播放引起的掉幀;
- 漸變背景圖層換成圖片;
- 加載下一頁數(shù)據(jù)代碼中主線程耗時(shí)方法,盡量放到非主線程的異步線程;
- 滑動(dòng)過程中,停止lottie動(dòng)畫;
- 優(yōu)化獲取size的邏輯:內(nèi)部控件固定的size計(jì)算好后緩存下來;
- 減少frame的設(shè)置刷新;
- 播放器代碼從UI組件抽取出來;
- 解決一個(gè)歷史遺留bug;
- 顯示封面圖時(shí)增加漸顯動(dòng)畫
優(yōu)化總結(jié)
優(yōu)化項(xiàng)總結(jié)
掉幀和卡頓相關(guān)優(yōu)化
- 修改Cell上UI控件反復(fù)的添加和刪除邏輯;
-
- cell是復(fù)用的,已經(jīng)添加在cell的控件,如果刷新數(shù)據(jù)時(shí)需要隱藏的使用hidden而不是重復(fù)remove/add(這樣修改測(cè)試重點(diǎn)要看下會(huì)不會(huì)因?yàn)閏ell的復(fù)用導(dǎo)致數(shù)據(jù)顯示沒有更新過來);
- lottie的url獲取和創(chuàng)建優(yōu)化:url是從config server獲取的,之前是每次刷新數(shù)據(jù)都獲取,改成創(chuàng)建控件的時(shí)候獲取一次;創(chuàng)建之前也是每次刷新數(shù)據(jù)的收獲都重新創(chuàng)建,改成只創(chuàng)建一次;
- 解決離屏渲染問題;
- 商品圖片和介紹圖預(yù)加載并裁減來減少體積(圖片從100~200Byte,減少到幾個(gè)Byte);
- 解決滑動(dòng)過程中停止播放器定時(shí)器生效導(dǎo)致播放器一直在播放引起的掉幀;(這是個(gè)程序Bug,是造成滑動(dòng)列表過程中掉幀很重要很隱蔽也是個(gè)很干擾排查的原因?。?/li>
- 漸變背景圖層換成圖片;
- 排查加載下一頁數(shù)據(jù)代碼中主線程耗時(shí)方法,盡量放到非主線程的異步線程;
- 滑動(dòng)過程中,停止lottie動(dòng)畫;
- 優(yōu)化獲取size的邏輯:內(nèi)部控件固定的size計(jì)算好后緩存下來;
- 減少frame的設(shè)置刷新;
體驗(yàn)優(yōu)化
- 顯示封面圖時(shí)增加漸顯動(dòng)畫
其他優(yōu)化
- 播放器代碼從UI組件抽取出來;
- 相同模版合并;
改善結(jié)果
改善前:無論是下拉加載數(shù)據(jù),還是數(shù)據(jù)加載完畢往上滑動(dòng)列表,都有明顯掉幀和卡頓現(xiàn)象
改善后:往上滑動(dòng)或者數(shù)據(jù)加載完畢再往下滑動(dòng),完全不會(huì)掉幀和卡頓
遺留問題
往下加載數(shù)據(jù)偶爾會(huì)出現(xiàn)掉幀現(xiàn)象。
分析可能的原因是往下滑動(dòng)加載下一頁數(shù)據(jù)時(shí),接口數(shù)據(jù)回調(diào)回來后調(diào)用框架的CSCollectionViewDataManager類的addCardInstances方法在主線程耗時(shí)27ms左右導(dǎo)致掉幀。
心得體會(huì)
- 因?yàn)橐鸬魩囊蛩厥嵌喾矫娴?,一開始無從查起;可以先把最明顯的一些優(yōu)化點(diǎn)先改好,再慢慢用排除法去定位哪些模塊引起問題的,從而再進(jìn)一步針對(duì)的排查;
- 重構(gòu)完一個(gè)單元,進(jìn)行一次自測(cè);
- 從明顯的關(guān)鍵問題入手分析改造;
- 采用排除法排除有問題的地方能快速定位問題,但是要注意排除干擾因素;比如排除抓包干擾,抓包的時(shí)候界面會(huì)變的更加卡頓掉幀,所以記得關(guān)掉antbox或者刪除掉抓包相關(guān)的依賴庫;
- 排查過程中,確保單一場(chǎng)景。比如列表往下滑和往上滑其實(shí)是不同的場(chǎng)景(往下拉時(shí)會(huì)觸發(fā)加載更多數(shù)據(jù)等一系列的動(dòng)作),應(yīng)該分別去分析兩個(gè)場(chǎng)景下引起掉幀的原因。
其他雜記
排查法檢查卡頓點(diǎn)過程記錄
單元模塊排除法找出引起掉幀的主要因素
場(chǎng)景:
1、往下拉
2、往上(回)拉
前提:注釋播放器play和stop邏輯;注釋起播加速;
步驟
1、注釋topview所有組件,只留下bottomview
測(cè)試滑動(dòng)效果:
往下滑動(dòng)時(shí),仍然會(huì)掉幀
網(wǎng)上滑動(dòng),不掉幀
結(jié)論:?
1、可能商品圖片加載引起掉幀
2、加載新數(shù)據(jù)引起掉幀
進(jìn)一步測(cè)試:注釋商品圖片加載代碼
測(cè)試滑動(dòng)效果:
往下滑動(dòng)加載新數(shù)據(jù),還是會(huì)有些許掉幀 —— 加載新數(shù)據(jù)有可能存在阻塞主線程的方法
往上滑動(dòng)不掉幀
2、 放出_introView(圖片和漸變背景先隱藏)
測(cè)試滑動(dòng)效果:
往下滑動(dòng),加載新數(shù)據(jù)有些掉幀;
往上滑動(dòng),基本不掉幀;再次往下滑動(dòng),也基本不掉幀了;
3、 放出tagview(lottie先隱藏)
測(cè)試滑動(dòng)效果:同上
4、 放出tagview的lottie
說明:
1、 lottie的url從config server取的,初始化控件的時(shí)候取一次
2、 lottie的創(chuàng)建(調(diào)用框架的方法,異步獲取, 并開啟了緩存(降級(jí)場(chǎng)景只能取到靜態(tài)圖?))
測(cè)試滑動(dòng)效果:同上
5、 放出_introView的漸變背景
測(cè)試滑動(dòng)效果:同上
6、 釋放封面圖預(yù)加載代碼
測(cè)試滑動(dòng)效果:同上; 感覺往下加載新數(shù)據(jù)時(shí),卡頓更嚴(yán)重一些,等分析加載新數(shù)據(jù)卡頓原因時(shí)再進(jìn)一步分析;
7、 釋放goodsview圖片
測(cè)試滑動(dòng)效果:同上;
8、 釋放_(tái)introView圖片加載
測(cè)試滑動(dòng)效果:
往下加載新數(shù)據(jù)會(huì)掉幀;
往上滑動(dòng)也出現(xiàn)了偶爾掉幀;
9、重新隱藏_introView圖片加載再測(cè)試一遍
測(cè)試滑動(dòng)效果:往回拉時(shí)基本不掉幀。
得出結(jié)論:同時(shí)只顯示一張圖片時(shí),往回拉基本不掉幀。增加一張圖片往回拉出現(xiàn)掉幀。
10、只加載封面圖,隱藏其他圖片加載
測(cè)試滑動(dòng)效果:同2
11、釋放_(tái)introView圖片加載
測(cè)試滑動(dòng)效果:掉幀不明顯
12、釋放goodsview圖片加載
測(cè)試滑動(dòng)效果:往回掉幀
13、goods和intro的圖片都預(yù)加載
Goods預(yù)加載前224b -> 預(yù)加載 14b
Intro預(yù)加載前104b -> 預(yù)加載 8b
接下來,逐步分析加載新數(shù)據(jù)掉幀原因
方法:從日志臺(tái)打印的關(guān)鍵日志入手,找到往回拉和往下拉日志差別;找到了didTriggerPaging方法;從這個(gè)方法入手終于揪出了兩個(gè)主線程耗時(shí)方法
1、addCardInstances 耗時(shí)20多ms
2、preHandleCardInstances 平均耗時(shí)4~6ms
遺留的問題
- 記得看下預(yù)覽的cell是否有離屏渲染問題
- 預(yù)加載圖片兩次,調(diào)查一下原因(打了兩次log);
- 加載出錯(cuò)時(shí),重新加載邏輯貌似現(xiàn)在沒有;
- 啟播加速放到異步線程?— 發(fā)現(xiàn)不影響
- reloadWithThemeConfigVO應(yīng)該沒有必要每加載一次都去拉取,可以優(yōu)化
- addCardInstances耗時(shí)是否真的影響列表渲染?
- 播放器的播放會(huì)掉幀,注釋掉播放器,完全不掉幀 —— 播放器沒有停止播放,會(huì)引起掉幀
- 往下加載數(shù)據(jù):注釋掉預(yù)加載圖片的代碼還是會(huì)掉幀,說明預(yù)加載圖片不是引起掉幀的原因?
- MLPLiveRecommendView里也有個(gè)image,但是這樣的數(shù)據(jù)很少,沒必要放到預(yù)加載圖片
結(jié)論一二
解決播放器引起的掉幀問題(滑動(dòng)過程中沒有停止播放)
發(fā)現(xiàn)的問題
已解決:
- cell上的lottie動(dòng)畫組件沒有復(fù)用
- cell上有大量的removeFromSuperView可能導(dǎo)致卡頓,代碼可以優(yōu)化(是為了避免數(shù)據(jù)重復(fù),在prepareForReuse里使用,可以換成在這里刷新數(shù)據(jù)???)
- 圓角設(shè)置的方式導(dǎo)致離屏渲染
未解決:
- 滑動(dòng)結(jié)束,一堆的cell調(diào)用了stop
- feeds接口請(qǐng)求參數(shù)exclContentIds隨著分頁數(shù)增加,數(shù)組越來越大,是必要傳的嗎
- 一直刷新,內(nèi)存爆掉閃退(可能不是這個(gè)模塊的問題)
重構(gòu)風(fēng)險(xiǎn)點(diǎn)
- 1、preview類型數(shù)據(jù)沒有自測(cè)過,記得mock數(shù)據(jù)自測(cè)
- 2、有recommend的數(shù)據(jù)也沒自測(cè)過
- 3、為了本地跑真機(jī)在工程文件里去掉了一個(gè)purchase的勾選,并提交了
- 4、開關(guān)mock數(shù)據(jù)測(cè)試一下開關(guān)是否生效
- 5、測(cè)試一下開關(guān)是在什么場(chǎng)景下生效(每次進(jìn)入直播廣場(chǎng)?)
- 6、偶現(xiàn)刷著刷著閃退
待解決的問題
- 1、找到檢測(cè)卡頓的工具 —— instruments的animation hitch 用iphone7(iOS14)可以錄制
新學(xué)到的知識(shí)點(diǎn)
- 同一父視圖重復(fù)調(diào)用addSubviews添加同一個(gè)View并不會(huì)產(chǎn)生多層級(jí)
- 測(cè)試性能時(shí)不要鏈接抓包工具(可刪除APMockData庫減少干擾);最好斷開debug鏈接;
- 三板斧:可監(jiān)控(日志、埋點(diǎn))、可灰度(發(fā)布階段)、可回滾(開關(guān)控制)
- mPaas框架
- ARC下block不需要copy,因?yàn)閷傩允莝trong,系統(tǒng)會(huì)將block自動(dòng)拷貝到堆區(qū),就不會(huì)有block捕獲的變量值不確定的問題了
- alpha屬性
1、 =0時(shí),不會(huì)從響應(yīng)鏈中移除
2、更改默認(rèn)是有動(dòng)畫的
3、 hidden隱藏性能更高
4、會(huì)影響自己和subview的透明度
- opaque: 給繪圖系統(tǒng)提供一個(gè)性能優(yōu)化開關(guān)
1、UIView當(dāng)有背景顏色時(shí):并且背景顏色有透明度(透明度不為1時(shí)),將 opaque設(shè)置為YES性能較高。
2、UIVIew有背景顏色時(shí):并且背景顏色的透明度為1,opaque的值不影 響性能。
3、UIVIew沒有背景顏色時(shí):opaque的值不影響性能。
- CALayer的opacity
耗時(shí)點(diǎn)
- 卡頓檢測(cè)工具使用測(cè)試
- 代碼熟悉過程,Cell模版注入原理(多虧師兄的指點(diǎn),縮短了理解時(shí)間)
- 每修改完一個(gè)組件,需要花大量時(shí)間mock數(shù)據(jù)模擬各種場(chǎng)景和參數(shù)自測(cè)
- 采用排除法測(cè)試是哪個(gè)組件導(dǎo)致卡頓時(shí),注冊(cè)掉一個(gè)加載圖片的組件,發(fā)現(xiàn)流暢很多;但是分析代碼不知道什么原因?qū)е驴D,然后一頓排除測(cè)試:
懷疑是imageview的背景色設(shè)置MLP_C_FFFFFF_NODARK導(dǎo)致的
懷疑是圖片資源的問題,更換了資源確實(shí)卡頓好很多
但是,最后發(fā)現(xiàn)斷開antbox連接后卡頓好很多(可刪除APMockData庫減少干擾) - 編譯瓶頸
-
- Xcode調(diào)試成為瓶頸(模擬器看離屏渲染有時(shí)跑不起來;修改一次代碼,編譯要好幾分鐘)
- 發(fā)現(xiàn)注釋代碼后,編譯時(shí)間變長;
- 取消注釋的代碼,編譯時(shí)間也很長;
- 什么代碼也沒改,重新編譯時(shí)間也很長;看來編譯時(shí)間可能跟網(wǎng)絡(luò)環(huán)境有關(guān)(同樣的電腦,同樣的工程,不一樣的網(wǎng)絡(luò)環(huán)境)
后記
參考文檔
Instruments性能檢測(cè)
Instruments性能檢測(cè) - 簡書
Animation Hitch:
iOS 底層原理39:Instruments系列(三)Animation Hitches - 簡書
iOS渲染流程和卡頓分析工具-Animation Hitches - 掘金
iOS 性能檢測(cè)新方式?——AnimationHitches - 知乎
iOS卡頓檢測(cè):
iOS卡頓檢測(cè) - 簡書
UICollectionView復(fù)用:
UICollectionView緩存機(jī)制探究 - 簡書
2016筆記——UICollectionView復(fù)用 - 簡書
dispatch_sync_on_main_queue:
如何安全使用dispatch_sync - 簡書
圓角設(shè)置:
iOS 設(shè)置圓角、指定位置圓角及 iOS 11圓角設(shè)置 - 簡書
離屏渲染:
iOS 離屏渲染探究 - 掘金
iOS圓角的離屏渲染,你真的弄明白了嗎 - 掘金
iOS 渲染原理解析:
iOS 渲染原理解析
iOS 離屏渲染原理及優(yōu)化(很有參考價(jià)值):
[轉(zhuǎn)] iOS離屏渲染原理及優(yōu)化 · Tenloy's Blog
設(shè)置圓角性能測(cè)評(píng):
iOS設(shè)置圓角的4種方法實(shí)例(附性能評(píng)測(cè))
卡頓原理和解決方案:iOS 深入理解列表卡頓原理和滑動(dòng)優(yōu)化方案_蘋果手機(jī)上的列表滑動(dòng)問題-CSDN博客文章來源:http://www.zghlxwxcb.cn/news/detail-814692.html
圖片的渲染過程與優(yōu)化(非常有用):iosiOS圖片的渲染過程與性能優(yōu)化 - 簡書文章來源地址http://www.zghlxwxcb.cn/news/detail-814692.html
到了這里,關(guān)于iOS UI掉幀和卡頓優(yōu)化解決方案記錄的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!