本文原文鏈接:http://www.cnblogs.com/liuling/p/2015-10-08-2.html
作者名:殘劍
如果一個布局十分復雜,那么就需要來排查是否出現了過度繪制,如果出現了,那么很可能會造成刷新率下降,造成卡頓的現象。那么什么是過度繪制呢?過度繪制就是在同一個區域中疊加了多個控件。這就像小時候我們畫畫,白紙就是沒有繪制的畫板,如果我們畫了一個房子,涂上了紅色,又在上面畫了窗戶,圖上了棕色,窗戶上又畫了藍色的玻璃,這重重復的疊加就是過度繪制,在白紙上的結果是,過度繪制的區域紙會被水筆浸的比較濕,在手機上就會出現顯示較慢。如果說這是感性的認識,那么我就引用下面一段話來理性的解釋一下:
1. 布局文件是一個xml文件,inflate布局文件其實就是解析xml,根據標簽信息創建相應的布局對象并做關聯。xml中的標簽和屬性設置越多,節點樹的深度越深,在解析時要執行的判斷邏輯、函數的嵌套和遞歸就越多,所以時間消耗越多;
2. inflate操作只是布局影響的第一個環節,一個界面要顯示出來,在requestLayout后還要執行一系列的measure、layout、draw的操作,每一步的執行時間都會受到布局本身的影響。而界面的最終顯示是所有這些操作完成后才實現的,所以如果布局質量差,會增加每一步操作的時間成本,最終顯示時間就會比較長。
現在,我們就來說說如何查看是否有過度繪制,和如何避免它吧。
一、查看是否存在過度繪制
1. GPU過渡繪制:對于過度繪制的測試主要通過人工進行測試,也是發現應用過渡繪制的首選途徑 .通過打開開發者選項中的 顯示GPU過度繪制(魅族手機:設置—輔助功能–開發人員工具–硬件加速渲染—調試GPU過渡繪制— 顯示過渡繪制區域.)來進行測試(PS:只有android4.2及以上的版本才具備此功能)
1. 顏色標識: 從好到差:藍-綠-淡紅-紅
1. 藍色1x過度繪制2. 綠色2x過度繪制3. 淡紅色3x過度繪制4. 紅色超過4x過度繪制
2. 驗收標準:
1. 控制過度繪制為2x2. 不允許存在4x過度繪制3. 不允許存在面積超過屏幕1/4區域的3x過度繪制(淡紅色區域)
從圖中我們就可以看到,文字部分出現了綠色(因為和底部的藍色疊加了,所以變成了黃綠色),在頂部開關部分出現了紅色,也就是四層的過度繪制,這是需要避免的。但由于在屏幕上占的位置很小,所以可以酌情考慮。
上面面是小米商店的截屏,可以看見其中有大量的過度繪制區域,總結下來過度繪制較常見于文字區域。
二、避免過度繪制的方法
下面這段文字來自他人博客:
作者:Gracker出處:androidperformance.com本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。打賞一下: 微博打賞
1. 盡量多使用RelativeLayout和LinearLayout, 不要使用絕對布局AbsoluteLayout,
1. 在布局層次一樣的情況下, 建議使用LinearLayout代替RelativeLayout, 因為LinearLayout性能要稍高一點.2. 在完成相對較復雜的布局時,建議使用RelativeLayout,RelativeLayout可以簡單實現LinearLayout嵌套才能實現的布局.
2. 將可復用的組件抽取出來并通過include標簽使用;3. 使用ViewStub標簽來加載一些不常用的布局;4. 動態地inflation view性能要比SetVisiblity性能要好.當然用VIewStub是最好的選擇.5. 使用merge標簽減少布局的嵌套層次6. 去掉多余的背景顏色
7. 對于有多層背景顏色的Layout來說,留最上面一層的顏色即可,其他底層的顏色都可以去掉8. 對于使用Selector當背景的Layout(比如ListView的Item,會使用Selector來標記點擊,選擇等不同的狀態),可以將normal狀態的color設置為”@Android:color/transparent”,來解決對應的問題
9. 內嵌使用包含layout_weight屬性的LinearLayout會在繪制時花費昂貴的系統資源,因為每一個子組件都需要被測量兩次。在使用ListView與GridView的時候,這個問題顯的尤其重要,因為子組件會重復被創建.所以要盡量避免使用Layout_weight10. 使得Layout寬而淺,而不是窄而深(在Hierarchy Viewer的Tree視圖里面體現)
上面提到的多個工具和技巧我都在之前的文章有所講解了,在實際開發過程中需要多多思考,根據情況來使用不同的技巧。
首先將講解一下GPU過渡繪制,也是開發者最直接接觸的部分吧,這個內容將分為兩個部分來將講,第一部分初步講解一下gpu過渡繪制的原理,和一些優化建議,第二部分將用實際例子來講解優化GPU過渡繪制的一般步驟。
過渡繪制概念
GPU過渡繪制的概念:GPU過度繪制指的是在屏幕一個像素上繪制多次(超過一次),比如一個TextView后有背景,那么顯示文本的像素至少繪了兩次,一次是背景,一次是文本。GPU過度繪制或多或少對性能有些影響,設備的內存帶寬是有限的,當過度繪制導致應用需要更多的帶寬(超過了可用帶寬)的時候性能就會降低。帶寬的限制每個設備都可能是不一樣的。
過渡繪制的原因
太多的View疊加復雜的層級疊加更長的inflation時間過渡繪制和不合理的xml布局的影響
布局文件是一個xml文件,inflate布局文件其實就是解析xml,根據標簽信息創建相應的布局對象并做關聯。xml中的標簽和屬性設置越多,節點樹的深度越深,在解析時要執行的判斷邏輯、函數的嵌套和遞歸就越多,所以時間消耗越多;inflate操作只是布局影響的第一個環節,一個界面要顯示出來,在requestLayout后還要執行一系列的measure、layout、draw的操作,每一步的執行時間都會受到布局本身的影響。而界面的最終顯示是所有這些操作完成后才實現的,所以如果布局質量差,會增加每一步操作的時間成本,最終顯示時間就會比較長。過渡繪制的一些基本概念:
Android提供了三個工具來幫助辨別和解決重繪問題:Hierachy Viewer,Tracer for OpenGL和Show GPU overdraw。前兩個可以在ADT工具或者獨立的monitor工具中找到,最后一個是在開發者選項的一部分.
GPU過渡繪制測試:對于過度繪制的測試主要通過人工進行測試,也是發現應用過渡繪制的首選途徑 .通過打開開發者選項中的 顯示GPU過度繪制(魅族手機:設置—輔助功能–開發人員工具–硬件加速渲染—調試GPU過渡繪制— 顯示過渡繪制區域. (魅族手機需要打開開發者模式:需要在電話界面輸入: ##6961## )) 來進行測試(PS:只有android4.2及以上的版本才具備此功能)顏色標識: GPU過渡繪制從好到差:藍-綠-淡紅-紅藍色1x過度繪制綠色2x過度繪制淡紅色3x過度繪制紅色超過4x過度繪制驗收標準:控制過度繪制為2x不允許存在4x過度繪制不允許存在面積超過屏幕1/4區域的3x過度繪制(淡紅色區域)優化工具介紹
Lint工具:Eclipse中,點擊即可,下面的窗口中會出現提示,根據提示和具體解決辦法消除.Android Studio自帶Lint工具,不合理或者需要優化和注意的地方,會用黃色標記出來.Lint工具不僅對布局有很好的優化建議,對代碼中不合理的活著存在潛在風險的模塊也會提出優化建議,所以一個好的建議是:多使用Lint工具檢查自己的應用,盡量消除所有的建議.Lint工具可以用命令行來運行,具體使用可以參考:tools.android.comLint工具的提升例子(摘自官方文檔):
Use compound drawables(使用compound drawables) - A LinearLayout which contains an ImageView and a TextView can be more efficiently handled as a compound drawable.Merge root frame(使用Merge根框架) - If a FrameLayout is the root of a layout and does not PRovide background or padding etc, it can be replaced with a merge tag which is slightly more efficient.Useless leaf(去除無用的分支) - A layout that has no children or no background can often be removed (since it is invisible) for a flatter and more efficient layout hierarchy.Useless parent (去除無用的父控件)- A layout with children that has no siblings, is not a ScrollView or a root layout, and does not have a background, can be removed and have its children moved directly into the parent for a flatter and more efficient layout hierarchy.Deep layouts (注意Layout的深度) - Layouts with too much nesting are bad for performance. Consider using flatter layouts such as RelativeLayout or GridLayout to improve performance. The default maximum depth isHierarchy Viewer:此工具是一個ADT工具(或者monitor,最新版本的SDK建議不使用獨立的HV工具,而是直接在monitor中進行操作.)的一部分,可以被用作對視圖層級進行快速解讀。在處理布局問題時特別有用,對于性能問題也很適用。Hierarchy Viewer默認只能在非加密設備使用,例如工程機,工程平板或者模擬器。為了能夠在任何手機上使用Hierarchy Viewer,你得在你的應用中添加ViewServer,這是一個開源庫,使用方法可以參考這里。連接上設備,打開Hierarchy Viewer(定位到tools/目錄下,直接執行hierarchyviewer的命令,選定需要查看的Process,再點擊Load View Hierarchy會顯示出當前界面的布局Tree。在每個模塊的Traffic light上有三個燈,分別代表了Measure, Layout and Draw三個步驟的性能。
布局優化建議
在Android UI布局過程中,通過遵守一些慣用、有效的布局原則,我們可以制作出高效且復用性高的UI,概括來說包括如下幾點:
盡量多使用RelativeLayout和LinearLayout, 不要使用絕對布局AbsoluteLayout,
在布局層次一樣的情況下, 建議使用LinearLayout代替RelativeLayout, 因為LinearLayout性能要稍高一點.在完成相對較復雜的布局時,建議使用RelativeLayout,RelativeLayout可以簡單實現LinearLayout嵌套才能實現的布局.將可復用的組件抽取出來并通過include標簽使用;
使用ViewStub標簽來加載一些不常用的布局;動態地inflation view性能要比SetVisiblity性能要好.當然用VIewStub是最好的選擇.使用merge標簽減少布局的嵌套層次去掉多余的背景顏色(查看背景顏色是否多余,可以將HierarchyView中的圖導出為PSD文件,然后用photoshop查看.具體可以參考這個視頻)
對于有多層背景顏色的Layout來說,留最上面一層的顏色即可,其他底層的顏色都可以去掉對于使用Selector當背景的Layout(比如ListView的Item,會使用Selector來標記點擊,選擇等不同的狀態),可以將normal狀態的color設置為/”@android:color/transparent”,來解決對應的問題內嵌使用包含layout_weight屬性的LinearLayout會在繪制時花費昂貴的系統資源,因為每一個子組件都需要被測量兩次。在使用ListView與GridView的時候,這個問題顯的尤其重要,因為子組件會重復被創建.所以要盡量避免使用Layout_weight
使得Layout寬而淺,而不是窄而深(在Hierarchy Viewer的Tree視圖里面體現)源碼相關
另外有能力看源碼的同學,下面是繪制OverDraw的源碼位置:/frameworks/base/libs/hwui/OpenGLRenderer.cpp,有興趣的可以去研究研究。
123456789101112131415161718192021222324252627282930313233343536373839404142 void OpenGLRenderer::renderOverdraw() { if (mCaches.debugOverdraw && getTargetFbo() == 0) { const Rect* clip = &mTilingClip; mCaches.enableScissor(); mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom, clip->right - clip->left, clip->bottom - clip->top); // 1x overdraw mCaches.stencil.enableDebugTest(2); drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode); // 2x overdraw mCaches.stencil.enableDebugTest(3); drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode); // 3x overdraw mCaches.stencil.enableDebugTest(4); drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode); // 4x overdraw and higher mCaches.stencil.enableDebugTest(4, true); drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode); mCaches.stencil.disable(); }}void OpenGLRenderer::countOverdraw() { size_t count = mWidth * mHeight; uint32_t* buffer = new uint32_t[count]; glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); size_t total = 0; for (size_t i = 0; i < count; i++) { total += buffer[i] & 0xff; } mOverdraw = total / float(count); delete[] buffer;}還有QA可能用得到的一個指標:OverDraw數值,這個的源碼位置在Framework/base/core/java/android/view/HardwareRender.java中(5.0中去掉了這個數值的顯示)
1234567891011121314151617181920212223242526272829303132333435363738 private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty, HardwareCanvas canvas, DisplayList displayList) { if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) { if (mDebugOverdrawLayer == null) { mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true); } else if (mDebugOverdrawLayer.getWidth() != mWidth || mDebugOverdrawLayer.getHeight() != mHeight) { mDebugOverdrawLayer.resize(mWidth, mHeight); } if (!mDebugOverdrawLayer.isValid()) { mDebugOverdraw = -1; return; } HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty); countOverdraw(layerCanvas); final int restoreCount = layerCanvas.save(); layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN); layerCanvas.restoreToCount(restoreCount); mDebugOverdrawLayer.end(canvas); float overdraw = getOverdraw(layerCanvas); DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics(); drawOverdrawCounter(canvas, overdraw, metrics.density); }}private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) { final String text = String.format(/"%.2fx/", overdraw); final Paint paint = setupPaint(density); // HSBtoColor will clamp the values in the 0..1 range paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f)); canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);}參考文章
優化過程反編譯并添加gpu顯示http://developer.android.com/training/improving-layouts/optimizing-layout.html#Inspecthttp://developer.android.com/training/improving-layouts/reusing-layouts.htmlhttp://developer.android.com/training/improving-layouts/loading-ondemand.htmlhttp://developer.android.com/training/improving-layouts/smooth-scrolling.htmlhttp://developer.android.com/tools/help/hierarchy-viewer.htmlhttp://tools.android.com/tips/lint做設計的人很少能知道GPU過度繪制是個什么鬼?跟設計有什么關系?今天就讓大家了解了解,首先來普及一下Android開發者選項中的Debug GPU overdraw。
GPU過度繪制定義
如果你粉刷過一個房間或一所房子,就會知道給墻壁涂上顏色需要做大量的工作。假如你還要重新粉刷一次的話,第二次粉刷的顏色會覆蓋住第一次的顏色,第一次的顏色就永遠不可見了,等于你第一次粉刷做的大量工作就完全被浪費掉。這太可怕了。
同樣的道理,如果在你的應用程序中浪費精力去繪制一些東西同樣會產生性能問題。過度繪制這個名詞就是用來描述屏幕上一個像素在單個幀中被重繪了多少次。
GPU過度繪制就指的是在屏幕一個像素上繪制多次(超過一次),GPU過度繪制或多或少對性能有些影響。
GPU過度繪制分析
過度繪制其實是一個性能和設計的交叉點。我們在設計上追求很華麗的視覺效果,但一般來說這種視覺效果會采用非常多的層疊組件來實現,這時候就會帶來過度繪制的問題。我們再來看看具體顯示在Android界面層級關系:
當我們來繪制一個界面時,會有一個windows,然后是建立Activity,在Activity里可以建立多個view,或view group,view也可以嵌套view。這些組件從上到下分布,上面的組件是可以被用戶看見的,而在下面的組件是不可見的,但是我們依然要花很多時間去繪制那些不可見的組件,因為在某些時候,它也可能會顯示出來。
檢測過度繪制
如何查看是否過度繪制:
設置-開發者選項-調試GPU過度繪制-顯示過度繪制區域(過度渲染等,不同機器可能不同)
然后就可以看看你的應用是否存在過度繪制的情況了。
那么如何判斷界面是否存在過度繪制呢?
開啟后,點擊我們的應用,可以看到各種顏色的區域,其中:
最理想的是藍色,一個像素只繪制一次,合格的頁面繪制是白色、藍色為主,綠色以上區域不能超過整個的三分之一,顏色越淺越好。
那么從設計的角度來看你的應用是否GPU繪制過度,看一下以下幾個界面:
從上圖我們可以看出:
Google now頁面GPU繪制比較正?;径际窃?x-2x范圍內,QQ的繪制情況也還可以,2345手機助手和2345影視大全過度繪制是很嚴重的,基本都是超過3x,4x。
可能有些人覺得不以為然,覺得沒什么影響。話又說回來,GPU繪制過渡對應用造成什么影響。
實際上,GPU繪制影響的是界面的流暢度和用戶體驗,對于好的手機可能體驗不到差距,對于差的手機,流暢度卻起著關鍵的作用。
可以大部分設計師關注的都是開發是否有100%還原你的設計稿,應用的交互體驗是否良好,沒有幾個設計師會去關注GPU過度繪制問題。
本人接觸到這方面知識也是優化我們開發人員指導,當時我們正在做界面層級簡化,而應用界面出現了2種背景顏色,如圖(左邊的背景為白色,右邊的背景為淺灰)。
開發讓我們梳理一下頁面層級,得出右邊的管理頁面要再多繪制一層;
開發人員建議我們以后在設計中,梳理一下頁面之前層級關系,盡量保持整個界面的架構統一,大背景色一致性。而且開發人員在開發過程中,盡量用簡化的結構來布局,保持界面還原度的同時也要考慮界面的流暢度。
列舉一下我們2345王牌助手首頁界面前后優化對比,看了以后,感覺整個人的心情都好了有木有啊,感覺更高大上了很多。
經過優化后的頁面一次繪制時間能提升3ms-5ms,可以看下面這張圖來進行對比,綠色色塊部分為提升空間。
這次優化工作對我來說,是一次寶貴的經驗,我們一直在努力讓設計變得更好,但有時候設計不只是表面的美化工作,還是深度的改善用戶體驗。在這里我想感慨一下,一個項目的成功上線,里面不知道有多少酸甜苦辣,不知道有多少人在為之努力,感謝所有工作人員的不懈努力,辛苦你們啦!
總結原因:
1.太多重疊的背景
重疊著的背景有時候是有必要的,有時候是沒必要的。這要視你的項目具體情況而定。
2.太多疊加的View
或者本來這個UI布局就很復雜或者你是為了追求一個炫麗的視覺效果,這都有可能使得很多view疊加在一起。這個情況非常普遍,下面的建議中會談談怎么減少這種情況帶來的影響。
3.復雜的Layout層級
復雜的層級關系,這個在布局中也很常見,下面也會說這種情況怎么做可以盡可能的減少過度繪制。
建議:
1.太多重疊的背景
這個問題其實最容易解決,建議前期在設計時盡量保持整體背景統一,另外開發可以檢查你在布局和代碼中設置的背景,有些背景是被隱藏在底下的,它永遠不可能顯示出來,這種沒必要的背景一定要移除,因為它很可能會嚴重影響到app的性能。
2.太多重疊的view
第一個建議是:使用ViewStub來加載一些不常用的布局,它是一個輕量級且默認不可見的視圖,可以動態的加載一個布局,只有你用到這個重疊著的view的時候才加載,推遲加載的時間。第二個建議是:如果使用了類似viewpager+Fragment這樣的組合或者有多個Fragment在一個界面上,需要控制Fragment的顯示和隱藏,盡量使用動態地Inflation view,它的性能要比SetVisiblity好。
3.復雜的Layout層級
這里的建議比較多一些,首先推薦用Android提供的布局工具Hierarchy Viewer來檢查和優化布局。第一個建議是:如果嵌套的線性布局加深了布局層次,可以使用相對布局來取代。第二個建議是:用標簽來合并布局,這可以減少布局層次。第三個建議是:用標簽來重用布局,抽取通用的布局可以讓布局的邏輯更清晰明了。記住,這些建議的最終目的都是使得你的Layout在Hierarchy Viewer里變得寬而淺,而不是窄而深。
新聞熱點
疑難解答