關於UI卡頓面試詳解

一、UI卡頓原理

        首先我們先看一下這樣兩個數字   :60fps- >16ms.

  其實很多用戶感覺到的卡頓問題最主要的根源是來自渲染性,在開發過程當中,大家在和UI溝通的時候,能感覺到這些UI特別希望他們的APP能有更絢麗的動畫,更精美的設計,同時還用一些很大的圖片來展現時尚元素,來增加用戶的體驗.但是UI設計師是站在UI的角度來考慮問題的,他們不知道安卓系統有時候可能無法完成複雜的界面渲染操作.我們知道安卓的系統,它每隔16毫秒它會發出信號,觸發對UI進行渲染,如果每次渲染都成功,這樣就能達到流暢的畫面所需要的60fps,也就是每秒60幀.爲了能夠實現60fps,這就意味着程序的大多數操作必須在16毫秒內完成,也就是拿1000除以60,約等於16毫秒.在執行一些動畫,或者listview,recycleview滑動的時候,通常能感覺到,有時候會有一些卡頓和不流暢.這就是這裏的操作很複雜,然後產生的丟幀現象導致的卡頓.其實有很多原因會造成卡頓.就簡單舉例,拿listView來說,listView的item和layout如果太過於複雜.它就無法完成在16毫秒內的渲染,也有可能是你的item上層疊了太多的bindground,其它太多的imageview.甚至還有可能是動畫執行的次數過多,這些都會造成cpu和gpu的負載過重,具體到我們60fps->16ms,我們想,爲什麼要把標準設置到60fps,首先我們要知道人腦對於畫面的連貫性,它是有一定的限制的,對於手機來說,我們需要感知屏幕操作連貫性,而安卓系統把這種流暢的貞率規定在60fps,60fps就是每秒實現的幀數,換算過來就是60毫秒一幀,也就是一秒60幀,所以說爲了保證不丟失幀數,我們一定要在16毫秒內處理完這次所有的cpu和gpu的計算,繪製和渲染操作,所以說UI卡頓它是可以進行量化的,每一次能否成功渲染是非常重要的,16毫秒是一個很大的一個標準,能否完整的做完一個操作,直接決定了卡頓性能問題,同時我們還需要知道,每次虛擬機進行GC的時候,所有的線程它都會暫停,當GC完了之後,所有的線程才能夠繼續進行,也就是說當這個16毫秒內進行渲染的時候,正好如果遇到了大量的GC操作,會導致渲染時間不夠,從而導致卡頓問題,大量的GC,我們知道也會造成內存抖動,所以說GC對象內存分配需要進行考慮的.

       接下來我們看 overdraw

      overdraw中文意思是過度繪製,它的意思是在屏幕上它的某個像素在同一幀的時間內被繪製了很多次,經常出現在多層次的UI結構裏面,如果有時候你把UI設置成GONE和invisible的時候,它也會做繪製的操作,這就導致了某些像素會被繪製很多次,這樣就浪費了CPU和GPU的資源,我們在開發過程中,如果老大給你分配一個,這時候讓你解決一個UI卡頓問題,你肯定回去用我們手機當中的GPU選項,也就是開發者選項當中,大家可以觀察overdraw情況,有藍色,淡綠色,淡紅色,深紅色.大家主要的目標就是要減少紅色,儘量出現藍色,最後我給大家強調一下,overdraw,overdraw出現的原因就是說你的UI佈局當中有大量的重疊的部分,還有的時候,是非必要重疊背景,舉例一下,就是activity中有一個背景,如果裏面的layout有自己的背景,同時layout中的子View又有自己的背景,這時候,你僅僅通過移除非必須的背景圖片,就能夠減少紅色的overdraw區,就能夠提升程序的性能,減少UI的卡頓.

       我來總結一下UI卡頓的原理,UI卡頓這些性能問題主要根源來自與安卓系統的渲染性能做了太多的耗時操作,做了太多耗時操作原因,有可能是你的layout 太複雜,也有可能是你的UI上層疊了太多其它layout佈局,還有就是你的動畫執行次數過多,

二、UI卡頓原因分析

       1.人爲在UI線程中做輕微耗時操作,導致UI線程卡頓;

我們知道安卓不是線程安全的,所以說你的耗時操作不能在主線程中進行操作的,你必須開啓一個工作線程來執行異步任務,我們大家知道經常用到的進程通訊handle,它可以進行線程之間消息的發送,所以說你在UI線程中做了輕微的耗時操作,注意是輕微的,不少耗時操作,如果你做了耗時操作,就不僅僅是UI卡頓了,UI卡頓其實是輕量版的ANR.如果你做了耗時操作你就會導致ANR,所以說這裏的前提是你在UI線程中做了會引起卡頓但是不至於ANR的輕微耗時操作,但是這也是很影響性能的,所以大家一定要避免.

       2.佈局Layout過於複雜,無法在16ms內完成渲染

這裏關於Layout佈局,其實我們在開發的時候,其實我們可以和UI設計師很好的去平衡這個佈局真正要顯示的內容,要知道他們的痛點,開發時,一定要注意背景佈局一定不能重疊,background的設置和子view的設置一定要謹慎考慮不能造成UI卡頓.

       3.同一時間動畫執行的次數過多,導致CPU或GPU負載過重

我們知道絢麗的動畫是可以吸引用戶,拉高你的存活,但是對我們手機的性能也是一個很大的考驗,所以說對動畫的選擇,我們需要仔細的平衡一下

       4.View過度繪製,導致某些像素在同一幀時間內被繪製多次,從而使CPU或GPU負載過重

這個其實和第二點是類似的,第二點是從佈局上,這一點是從代碼上考慮的,這是大家一定需要注意的.

      5.View頻繁的觸發measure、layout,導致measure、layout累計耗時過多以及整個View頻繁的重新渲染

我們知道View的繪製大體是需要經過,measure,layout和draw這三個方法l來進行的,所以說如果你的view如果頻繁的觸發了測量和繪製和擺放操作,就會導致整個性能耗時過多,從而導致它所需要的View頻繁的重新渲染,而影響整個的性能

      6.內存頻繁觸發GC過多,導致暫時阻塞渲染操作

大家在平時回收內存的時候,也需要考慮一下時機.

      7.冗餘資源以邏輯等導致加載和執行緩慢

這就是說在開發過程中,一些代碼的邏輯,代碼的啓動順序,以及一些異步任務的處理,它會導致整個的UI卡頓.所以說對於代碼的優化也能影響卡頓,所以說良好的代碼開發習慣,也能提高APP的性能

      8.ANR

它產生的主要原因就是在主線程中做了耗時操作導致,程序不能響應,這是急需要避免的.應該說UI卡頓是輕量版的ANR, 但是不是說因爲它是輕量版的,就可以造成的,它還是需要避免的.

 

三、UI卡頓總結

         1.佈局優化

* 選擇耗費性能少的佈局
耗費性能低的佈局有FrameLayout、LinearLayout
高的有RelativeLayout;佈局過程消耗更多的CPU資源和時間
嵌套消耗的性能 > 單個佈局本身的性能

* 減少佈局的嵌套
佈局層級越少繪製的工作量越少,繪製速度越快,可以選擇merge標籤,減少佈局層級。
<merge>作爲佈局的根標籤時,其他佈局通過<include>標籤引用的時候,只有merge標籤內的內容被引用,這樣減少一層佈局。

* 提供佈局的複用性
提取不同佈局之間共同的部分,提高佈局的測量和繪製時間,使用include標籤。

* 減少初次測量&繪製時間
使用<ViewStub>標籤,輕量級View,不佔用顯示資源,默認不顯示,只有在需要時才顯示,比如顯示進度、信息出錯的提示佈局。
ViewStub標籤引入外部佈局,類似於include標籤。當調用ViewStub.inflate()時,ViewStub指向的佈局文件纔會被inflate、實例化,顯示出來。
注意:
~ ViewStub的layout佈局不能使用merge標籤,否則報錯
~ ViewStub的inflate只能執行一次,顯示之後,就不能在使用ViewStub控制。
~ 與View.setVisible(View.Gone)的區別:View 的可見性設置爲 gone 後,在inflate 時,該View 及其子View依然會被解析;而使用          ViewStub就能避免解析其中指定的佈局文件,從而節省佈局文件的解析時間 & 內存的佔用

* 儘可能少用佈局屬性wrap_content
佈局屬性 wrap_content 會增加布局測量時計算成本


佈局調優工具
常用的有hierarchy viewer、Lint、Systrace

Hierarchy Viewer
Android studio提供的UI性能檢測工具。

2.列表以及Adapter優化

這體現在listview上,我們儘量複用listView的adapter當中的gitview()方法,不要重複獲取實例,使用convertView,列表在滑動的時候你不要進行元素的更新,就是說在listView滑動的時候,你監聽它的事件只有它的滑動到停止的時候,你纔可以去加載圖片,加載數據,而它在滑動的時候,你可以只顯示圖片的默認值,或者縮略圖

3.背景和圖片等內存分配優化

就是說,你一定要儘量減少整個佈局當中,不必要的背景設置,圖片你要進行壓縮處理,還有就是對GC的處理上,一定要注意當圖片16ms內進行渲染的時候,所造成的一些頻繁GC回收,從而導致內存泄漏問題

4.避免ANR

不要在主線程做耗時操作,一定要開啓子線程去做耗時操作,常用我們安卓中提供的良好的異步消息處理框架,例如:

  • 直接 new Thread()
  • 用handle
  • 用Android自帶的AsyncTask
  • 用RxJava
  • 等等  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章