前一段時間,筆者帶你們一塊兒深刻探索Android佈局優化和深刻探索Android卡頓優化,內容難度比較大,所以,本篇文章就是上述兩篇文章的基礎篇,掌握這篇文章的知識後,閱讀上面兩篇文章的難度會小不少。html
咱們都知道,形成繪製不流暢最大的罪魁禍首就是卡頓,而卡頓的主要場景有不少,按場景能夠分紅4類:UI繪製、應用啓動、頁面跳轉、事件響應,其中又可細分爲以下:python
而形成其產生的根本緣由能夠分爲兩大類:android
Android的顯示過程能夠簡單歸納爲:Android應用程序把通過測量、佈局、繪製後的surface緩存數據、經過SurfaceFlinger把數據渲染到顯示屏幕上,經過Android的刷新機制來刷新數據。也就是說應用層負責繪製,系統層負責渲染,經過進程間通訊把應用層須要繪製的數據傳遞到系統層服務,系統層服務經過刷新機制把數據更新到屏幕。git
在Android的每一個View都會通過Measure和Layout來肯定當前須要繪製的View所在的大小和位置,而後,再經過Draw繪製到surface上。在Android系統中總體的繪製源碼是在ViewRootImpl類的performTraversals()方法,經過這個方法能夠看出Measure和Layout都是遞歸來獲取View的大小和位置,而且以深度做爲優先級。顯然,層級越深,元素越多,耗時就越長。github
對於繪製,Android支持兩種繪製方式:算法
硬件加速從Android 3.0開始支持,它在UI顯示和繪製效率方面遠高於軟件繪製。但它的侷限以下:shell
將數據渲染到屏幕上是經過系統級進程中的SurfaceFlinger服務來實現的,它的主要工做流程以下:json
其中,SurfaceFlinger系統進程和應用進程使用了匿名共享內存SharedClient,而且,每個應用和SurfaceFlinger之間都會建立一個SharedClient,在每一個SharedClient中,最多能夠建立31個SharedBufferStack,每個SharedBufferStack對應一個Surface,即一個window。(其中包含了兩個(小於4.1版本)或者三個(4.1及以上版本)緩衝區)canvas
所以,從上可知,一個Android應用程序最多能夠包含31個窗口。最後,顯示的總體流程以下:緩存
繪製的過程首先是CPU準備數據,經過Driver層把數據交給CPU渲染,其中CPU主要負責Measure、Layout、Record、Execute的數據計算工做,GPU負責Rasterization(柵格化)、渲染。由於圖形API不容許CPU直接和GPU通訊,因此要經過一個圖形驅動的中間層來進行鏈接,在圖形驅動裏面維護了一個隊列,CPU把display list(待顯示的數據列表)添加到隊列中,GPU從這個隊列中取出數據進行繪製,最終纔在顯示屏上顯示出來。
Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,若是每次渲染都成功,這樣就可以達到流暢畫面所需的60FPS。
在4.1版本的Project Butter中對Android Display系統進行了重構,引入了三個核心元素:VSYNC(Vertical Synchronization)、Triple Buffer(三級緩衝)、Choreographer。其中做爲Project Buffer核心的VSYNC,即垂直同步可認爲是一種定時中斷。而Choreographer起調度的做用,將繪製工做統一到VSYNC的某個時間點上,使應用的繪製工做有序。
目的是解決刷新不一樣步的問題。
在Tripe Buffer出現以前,Android的顯示系統採用的是雙緩衝技術。
在Linux上一般使用 Framebuffer 來作顯示輸出,當用戶進程更新Framebuffer中的數據後,顯示驅動會把FrameBuffer中每一個像素點的值更新到屏幕,可是若是上一幀數據還沒顯示完,Framebuffer中的數據又更新了,就會帶來殘影的問題,用戶會以爲有閃爍感,因此採用了雙緩衝技術。
雙緩衝意味着要使用兩個緩衝區(在上文說起的SharedBufferStack中),其中一個稱爲Front Buffer,另外一個稱爲Back Buffer。UI老是先在Back Buffer中繪製,而後再和Front Buffer交換,渲染到顯示設備中。即只有當另外一個buffer的數據準備好後,纔會經過io_ctl系統調用來通知顯示設備切換Buffer。
由於只有兩個Buffer;因此4.1版本後,出現了第三個緩衝區:Triple Buffer。它利用CPU/GPU的空閒等待時間提早準備好數據,並不必定會使用。
除非必要,大部分狀況下只是用到雙緩衝。並且,緩衝區並非越多越好,要作到平衡到最佳效果。
由於VSync 中斷處理的線程優先級必定要最高,不然即便接收到VSync中斷,不能及時處理,也是徒勞無功。
當收到VSYNC信號時,調用用戶設置的回調函數。回調類型的優先級從高到低爲CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL。
Android經常使用的繪製優化工具通常有以下幾種:
這裏咱們來說解後面三種分析工具。
它是Android手機上自帶的一個輔助工具,打開Profile GPU Rendering後能夠看到實時刷新的彩色圖,其中每一根豎線表示一幀,由多個顏色組成。
在Android M版本以前,每一條柱狀圖都由紅、黃、藍、紫組成,分別對應每一幀在不一樣階段的實際耗時不一樣顏色的解釋以下:
而且,從Android M開始變成了渲染八步驟:
表示GPU處理任務的時間。
進行2D渲染顯示列表的時間,越高表示須要繪製的視圖越多。
準備有待繪製的圖片所耗費的時間,越高表示圖片數量越多或圖片越大。
測量和繪製視圖所需的時間,越高表示視圖越多或onDraw方法有耗時操做。
onMeasure與onLayout所花費的時間。
執行動畫所須要花費的時間。越高表示使用了非官方動畫工具或執行中有讀寫操做。
系統處理輸入事件所耗費的時間。
主線程執行了太多任務,致使UI渲染跟不上vSync的信號而出現掉幀。
此外,可經過以下adb命令將具體的耗時輸出到日誌中來分析:
adb shell dumpsys gfxinfo com.**.**
複製代碼
它主要用來分析函數的調用過程,能夠對Android的應用程序以及Framework層代碼進行性能分析。
使用TraceView查看耗時,主要關注Calls + Recur Calls / Total和(該方法調用次數+遞歸次數)和Cpu Time / Call(該方法耗時)這兩個值,而後優化這些方法的邏輯和調用次數,減小耗時。
RealTime(實際時長)的實際執行時間要比CPU Time要長,由於它包括了CPU的上下文切換、阻塞、GC等時間消耗。
Systrace是Android 4.1及以上版本提供的性能數據採樣和分析工具,它的主要做用能夠歸結爲以下兩點:
使用事項以下:
通常咱們使用命令行來獲得輸出的html表單,在4.3版本及以上能夠省略設置跟蹤類別標籤來獲取默認值。命令以下:
cd android-sdk/platform-tools/systrace
python systrace.py --time=10 -o mynewtrace.html sched gfx view wm
複製代碼
其中,經常使用的幾個參數命令以下:
其他標籤用法請參見此處。
此外,咱們可使用代碼插樁的方式,在Android 4.3及以上版本可使用Trace類的Trace.beginSection()與Trace.endSection()方法來進行追蹤。其中須要注意:
使用Chrome打開文件後,其中和UI繪製關係最密切的是Alerts和Frame兩個數據:
最後,這裏再列出在Systrace便於操做的快捷鍵:
RelativeLayout也存在性能低的問題,緣由是RelativeLayout會對子View作兩次測量。但若是在LinearLayout中有weight屬性,也須要進行兩次測量,可是由於沒有更多的依賴關係,因此仍然會比RelativeLayout的效率高。
因爲Android的碎片化程度很高,因此使用RelativeLayout能使構建的佈局適應性更強。
merge的原理:在Android佈局的源碼中,若是是Merge標籤,那麼直接將其中的子元素添加到Merge標籤Parent中。
ViewStub是一個輕量級的View,它是一個看不見的,而且不佔佈局位置,佔用資源很是小的視圖對象。能夠爲ViewStub指定一個佈局,加載佈局時,只有ViewStub會被初始化,而後當ViewStub被設置爲可見時,或是調用了ViewStub.inflate()時,ViewStub所指向的佈局纔會被加載和實例化,而後ViewStub的佈局屬性都會傳給它指向的佈局。
注意:
Android的佈局複用能夠經過 include 標籤來實現。
最後,下面列出了我日常作佈局優化時的一些小技巧:
致使過分繪製的主要緣由通常有以下兩點:
打開手機開發者選項中的Show GPU Overdraw選項,會有不一樣的顏色來表示過分繪製次數,依次是無、藍、綠、淡紅、深紅,分別對應0-4次過分繪製。
好比:在獲取Avatar的圖像以後,把ImageView的Background設置爲Transparent,只有當圖像沒有獲取到時,才設置對應的Background佔位圖片。
經過canvas.clipRect()來幫助系統識別那些可見的區域。這個方法能夠指定一塊矩形區域,只有在這個區域內纔會被繪製。而且,它還能夠節約CPU和GPU資源,在clipRect區域以外的繪製指令都不會被執行。
在繪製一個單元以前,首先判斷該單元的區域是否在Canvas的剪切域內。若不在,直接返回,避免CPU和GPU的計算和渲染工做。
如經過監聽ListView的onScrollStateChanged事件,在滾動時暫停圖片下載線程工做,結束後再開始,能夠提升ListView的滾動平滑度,RecyclerView同理。
如自定義View通常採用invalidate方法刷新,可使用如下重載方法指定要刷新的區域:
提高動畫性能主要從如下三個緯度着手:
消耗資源最多,效果最差,能不用就不用。
使用補間動畫實現致使View重繪很是頻繁,更新DisplayList的次數過多,且有如下缺點:
相比於補間動畫,屬性動畫重繪明顯會少不少,應優先使用。
核心類:DisplayList,每個View對應一個。
在打開硬件渲染後繪製View時,其中執行繪製的draw()方法會把全部繪製命令記錄到一個新的顯示列表(DisplayList),這個顯示列表包含了輸出的View層級的繪製代碼,但並非加入到顯示列表就馬上執行,當這個ViewTree的DisplayList全都記錄完畢後,由OpenGLRender負責將Root View中的DisplayList渲染到屏幕上。而invalidate()方法只是在顯示列表中記錄和更新顯示層級,去標記不須要繪製的View。
若是應用程序中只使用了標準View或者Drawable,就能夠爲整個系統打開硬件加速的全局設置。
此時,會使用硬件紋理操做對一個View進行動畫繪製,若是不調用invalidate()方法,就能夠減小對View自身頻繁的重繪。同時Android 3.0的屬性動畫也減少了重繪,當View經過硬件層返回時,最終全部的層疊畫面顯示到屏幕,View的屬性同時被處理好,所以只要設置這些屬性,就能夠明顯提升繪製的效率,它們不須要View重繪,設置屬性後,View會自動刷新。所以,屬性動畫中繪製的遞歸次數比補間動畫少不少。
在Android 3.0前,使用View的繪製緩衝或Canvas.saveLayer()函數對離屏緩衝進行渲染。Android 3.0後則使用View.setLayerType(type, paint)方法代替,type能夠爲如下三種Layer類型之一:
一、將要執行動畫的View的LayerType設置爲LAYER_TYPE_HARDWARE。
二、計算動畫View的屬性等信息,更新View的屬性。
三、若動畫結束,將LayerType設置爲NONE。
目前比較流行的方案都是利用了Looper中的Printer來實現監控。
利用主線程的消息隊列處理機制,經過自定義Printer,而後在Printer中獲取到兩次被調用的時間差,這個時間差就是執行時間。若是該時間超過設定的卡頓閾值(如1000ms)時,主線程卡頓發生,並拋出各類有用信息,供開發者分析。(此外,也能夠在UI線程之外開啓一個異步線程,定時向UI線程發送一個任務,並記下發送時間。任務的內容是將執行時間同步到發送線程,若是UI線程被阻塞,那麼發送過去的任務不能被準時執行。但此方法會增長系統開銷,不可取)
發生卡頓時須要捕獲以下四類信息,以提升定位卡頓問題的效率與精度。
這裏的信息建議抽樣上報或者能夠先將其保存到本地,在合適的時機以及達到必定的量時,再壓縮上報到服務器,供開發者分析。具體監控代碼實現能夠參考BlockCanary開源項目的代碼。
至此,這裏咱們分析一下繪製優化應經歷的幾個過程:
應用之因此會出現卡頓,除了繪製方面的問題,還有一個影響因素就是內存,不合理地使用內存不只會致使卡頓,還會對耗電和應用的穩定性形成很大影響。下一篇性能優化文章,筆者將對Android中的內存優化進行全面的講解,若讀者以爲哪裏有寫的很差的地方或有誤的地方但願多多進行批評指正,願咱們共同進步和成長!
一、Android應用性能優化最佳實踐
二、必知必會 | Android 性能優化的方面方面都在這兒
歡迎關注個人微信:
bcce5360
微信羣若是不能掃碼加入,麻煩你們想進微信羣的朋友們,加我微信拉你進羣。
2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎你們加入~