性能優(yōu)化技巧知識梳理(3) - 如何排查列表卡頓問題

一、前言

如果現(xiàn)在用戶反饋某個列表很卡,你會怎么排查問題?

這樣一個簡短的問題,其實考察了我們多方面的知識。要答出其中的一兩個小點其實并不難,難的是如何能夠由外之內(nèi),由淺入深娓娓道來,它考察的是一個程序員發(fā)現(xiàn)問題、解決問題、歸納總結(jié)的能力。

要回答這個問題,可以從以下四個方面層層深入,整個大綱如下:

  • (1) 渲染原理
    • 為什么會感知到卡頓
    • 理解VSYNC
  • (2) 卡頓的外部因素
    • 手機性能
    • 系統(tǒng)本身
    • 內(nèi)存抖動
    • 在主線程執(zhí)行耗時操作
  • (3) 卡頓的內(nèi)部因素
    • 布局層級
    • measurelayout、draw耗時
    • 過度繪制
  • (4) 監(jiān)控卡頓
    • 使用Handler#setMessageLogging

這篇文章中穿插著介紹了性能優(yōu)化工具的使用場景,所有的鏈接地址為:

二、渲染原理

首先我們需要明白 為什么用戶會感知到卡頓,要回答這個問題,就需要對渲染的原理有一個基本的了解。

2.1 為什么會感知到卡頓

用戶感知到的卡頓主要的根源是因為渲染性能。Android系統(tǒng)每隔16ms發(fā)出VSYNC信號,觸發(fā)對UI進行渲染,如果每次渲染都成功,這樣就能夠達到所需要的60fps,為了能夠?qū)崿F(xiàn)60fps,這意味著程序的大多數(shù)操作都必須在16ms內(nèi)完成。

如果你的某個操作是24ms,系統(tǒng)在得到VSYNC信號的時候就無法進行正常的渲染,這樣就發(fā)生了丟幀現(xiàn)象,那么用戶在32ms內(nèi)看到的是同一幀畫面。

2.2 理解 VSYNC

在理解VSYNC之前,首先需要區(qū)分 幀率刷新率

  • 幀率:代表了GPU1s內(nèi) 繪制操作 的幀數(shù),例如30fps、60fps,屬于 軟件參數(shù)
  • 刷新率:代表了屏幕在1s內(nèi)刷新屏幕的次數(shù),這取決于 硬件的固定參數(shù),例如60Hz。

GPU獲取圖形數(shù)據(jù)進行渲染,然后硬件負責把渲染后的內(nèi)容呈現(xiàn)到屏幕上,兩者不停地協(xié)作。

當幀率和刷新率不一致的時候,就會發(fā)生畫面上下兩部分內(nèi)容斷裂,來自不同的兩幀數(shù)據(jù)發(fā)生重疊。因此引入了VSYNC,在超過60fps的情況下,GPU所產(chǎn)生的幀數(shù)據(jù)會因為等待VSYNC的刷新信號而被Hold住,這樣能夠保持每次刷新都有實際的新的數(shù)據(jù)可以顯示。

但是我們遇到更多的情況是 幀率小于刷新率,也就是我們通常所說的卡頓,如下圖所示。

三、卡頓的外部因素

卡頓的 外因 可以歸結(jié)為以下幾個方面:

  • 手機性能問題,CPU性能不足,內(nèi)存小。
  • 系統(tǒng)本身問題,所有應(yīng)用都很卡。
  • 頻繁觸發(fā)GC,導(dǎo)致內(nèi)存抖動。
  • 在主線程中進行了耗時的操作。

3.1 手機性能問題

通過排查反饋用戶的機型,如果大部分的反饋都是來自于低端機的用戶,那么可以與產(chǎn)品溝通,通過獲取硬件的相關(guān)參數(shù),例如CPU核數(shù)、內(nèi)存大小,對于這些低端機型進行特殊的處理,對需求進行簡化,避免去實現(xiàn)復(fù)雜的動畫效果。

3.2 系統(tǒng)本身問題

如果可以聯(lián)系用戶,并且用戶反饋不僅是我們的應(yīng)用,而是整個系統(tǒng)都很卡,那么對于我們來說,其實做不了什么。

如果無法聯(lián)系用戶,那么可以通過trace文件進行分析。

3.3 內(nèi)存抖動

內(nèi)存抖動指的是有大量的對象頻繁地進出內(nèi)存的新生代區(qū)域,它往往會伴隨著頻繁的GC,而GC會占用UI線程和CPU資源,從而導(dǎo)致應(yīng)用發(fā)生卡頓,因此我們需要盡量這種現(xiàn)象的發(fā)生。

3.3.1 排查內(nèi)存抖動

在排查內(nèi)存抖動問題的時候,我們可以通過以下幾個工具來輔助排查問題:

  • Memory Monitor:在列表滑動的時候,實時觀察內(nèi)存的分配情況,定位發(fā)生GC的時間點,確定其是否合理,但是其缺點是 無法列出具體的分配對象。
  • Heap Viewer:在垃圾回收的時候,呈現(xiàn)出某一時刻的內(nèi)存快照,幫助我們分析是哪個對象引起了內(nèi)存泄漏。
  • Allocation Tracker:分析出一段時間內(nèi)對象的分配情況,并列出是由什么邏輯導(dǎo)致了這個對象的分配,與Heap Viewer配合使用,來分析大對象產(chǎn)生的原因。

以上這三種工具的詳細使用可以看之前總結(jié)的這篇文章:性能優(yōu)化工具知識梳理(6) - Memory Monitor & Heap Viewer & Allocation Tracker。

3.3.2 容易發(fā)生內(nèi)存抖動的場景

在平時的開發(fā)中,我們可以使用以下幾點來避免內(nèi)存抖動的發(fā)生:

  • 在創(chuàng)建對象的操作,移出到循環(huán)體外
  • 不要在onMeasure、onLayout、onDraw方法中頻繁地創(chuàng)建對象,例如Paint、Path這樣的類。
  • 在使用Bitmap的時候,考慮通過LruCache+inBitmap的方式進行復(fù)用。
  • 合理地使用對象池來緩存對象。

3.4 在主線程中,執(zhí)行了耗時的操作

3.4.1 排查耗時操作

在排查主線程的耗時操作時,最常用的就是TraceView,通過這個工具可以看到每個方法的具體耗時時間,關(guān)于TraceView的詳細使用可以參考 性能優(yōu)化工具知識梳理(1) - TraceView 這篇文章。

3.4.2 解決主線程耗時問題

在解決主線程耗時問題時,需要根據(jù)具體的業(yè)務(wù)的場景來排查,一般來說,當我們遇到列表卡頓的問題,可以優(yōu)先從以下幾個重要的回調(diào)中排查,看下是否在其中執(zhí)行了耗時的操作,例如IO、JSON等。

  • RecyclerViewonBindViewHolder
  • ListViewgetView。
  • RecyclerView/ListViewonScrollChanged

四、卡頓的內(nèi)部因素

  • 布局層級
  • measure、layoutdraw的耗時
  • 過度繪制

4.1 布局層級

當我們設(shè)計列表的每個Item項時,應(yīng)當盡量減少每個Item的布局層級,因為布局層級越深,每個Item繪制就越耗時。

4.1.1 排查布局層級問題

在檢查布局層級問題時,通常是使用Hierarchy Viewer工具,通過該工具可以做到以下兩點:

  • 檢查每個Item項的布局層級
  • 通過每個節(jié)點的三個圓點顏色查看其在測量、布局、繪制三個階段的性能表現(xiàn),綠色表示OK,黃色表示其處于渲染速度比較慢的50%,紅色表示渲染速度非常慢。

更加詳細的介紹可以參考 性能優(yōu)化工具知識梳理(4) - Hierarchy Viewer。

4.1.2 減少布局層級

減少布局層級更多的是需要依賴開發(fā)者的習慣,因為有些時候,越少的層級往往需要更復(fù)雜的設(shè)計邏輯,這意味著需要花更多的時間來思考,在這里強烈推薦ConstraintLayout控件,對于任何復(fù)雜的場景,只需要一層就可以了,使用可以參考 ConstraintLayout 完全解析 快來優(yōu)化你的布局吧

對于減少布局層級,有以下幾點技巧:

  • 首先應(yīng)當考慮布局層級最小的方案。
  • 布局層級相同時,就應(yīng)當選取合適的父容器,一般來說,有以下幾點經(jīng)驗:
  • 選取的優(yōu)先級為:FrameLayout、不帶layout_weight參數(shù)的LinearLayout、RelativeLayout,這里選取的標準為帶有layout_weightLinearLayout或者RelativeLayout會測量兩次。
  • 當使用LinearLayout時,應(yīng)當盡量避免使用layout_weight參數(shù)。
  • 避免使用RelativeLayout嵌套RelativeLayout
  • 如果允許,那么可以使用GoogleConstraintLayout布局。

更多的技巧可以參考 性能優(yōu)化技巧知識梳理(1) - 布局優(yōu)化。

4.2 measure、layout、draw 的耗時時間

對于這三個階段的耗時,可以通過兩個工具來排查問題:

當我們發(fā)現(xiàn)在某個階段耗時過長時,就需要去排查是否在以上三個回調(diào)當中做了不當?shù)牟僮鳌?/p>

4.3 過度繪制

過度繪制其實是 布局層級過深的結(jié)果,通過設(shè)置中的 調(diào)試 GPU 過度繪制,可以直觀地看到繪制的重疊情況,檢測的結(jié)果分為以下四種,嚴重程度依次遞增:

  • 藍色
  • 綠色
  • 淺紅
  • 深紅

對于過度繪制的部分,需要想辦法去優(yōu)化,詳細的使用方式為:性能優(yōu)化工具知識梳理(3) - 調(diào)試GPU過度繪制 & GPU呈現(xiàn)模式分析。

五、監(jiān)控卡頓

在前面兩節(jié)中,我們從外因和內(nèi)因兩個部分總結(jié)了卡頓問題的排查方法和注意事項,除此之外,還可以通過一些手段實時地監(jiān)控卡頓問題,這里推薦使用HandlersetMessageLogging方法,檢測每個消息的耗時時間,當其耗時大于閾值的時候,輸出堆棧信息。

簡單的實現(xiàn)方式為 Framework 源碼解析知識梳理(4) - 從源碼角度談?wù)?Handler 的應(yīng)用

BlockCanary就是基于這個原理來實現(xiàn)的,具體的使用方式可以參考 AndroidPerformanceMonitor。

六、參考文獻

1. Android 性能優(yōu)化典范 - 第1季
2. ConstraintLayout 完全解析 快來優(yōu)化你的布局吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容