閱讀402 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Android性能優化案例研究(下)

轉自 https://www.importnew.com/4065.html



譯者前言:在Android性能優化案例研究(上)中,作者Romain Guy將Falcon Pro這款應用作為例子,通過Android現有的工具追蹤和分析了其隱藏的性能問題(重繪)。下篇作者將會帶來如何解決此類問題的方法和思路。

去掉冗餘的圖層

為 了去掉重繪我們必須首先理解它從哪裏產生的。這就輪到Hierarchy Viewer和Tracer for OpenGL大顯身手的時候了。Hierarchy Viewer是ADT工具(或者monitor)的一部分,可以被用作對視圖層級進行快速解讀。在處理布局問題時特別有用,對於性能問題也很適用。

 

重要:

Hierarchy Viewer默認隻能在非加密設備使用,例如工程機,工程平板或者模擬器。為了能夠在任何手機上使用Hierarchy Viewer,你得在你的應用中添加ViewServer,這是一個開源庫。

在 ADT(或者monitor)中打開Hierarchy Viewer的全景圖,選擇window標簽。這個界麵就會粗體高亮的顯示當前設備運行的窗口,通常就是你想要研究的那個應用。選中它再點擊工具欄的 Load按鈕(它更像藍色方塊組成的樹)。加載這棵樹需要一段時間,所以請耐心等待。當這棵樹加載完成你就可以看到如下圖所示的畫麵。

 

 

現 在視圖的層級已經加載到工具裏,我們也可以將其轉換為PhotoShop文檔。隻要點擊工具欄的第二個按鈕,工具提示說:“Capture the window layers [..]”。Adobe Photoshop本身不是必須的,因為生成的文檔可以被其他工具兼容,例如Pixelmator, The GIMP等等。你們可以下載我所生成的PSD文件

PhotoShop文檔可以顯示每個視圖的每個圖層。一個圖層可以標記為可見或者不可見,這是取決於View.getVisibility()返回的結果。每一個圖層命名在一個視圖的後麵,如果視圖的android:id存在則使用android:id,或者使用它的類名。我曾經開始添加對於組(group)的支持用於視圖樹的重建…我其實應該早點把這個功能做完。

 

 

通過檢查每個圖層的列表,我們可以快速的辨別至少一種重繪的源頭:多個全屏的背景。第一個就是第一個圖層,叫做DecorView。這個view是由Android框架生成的,包含了皮膚主題指定的背景。這個默認的背景在應用中是不可見的,因此它可以被安全的去掉。

 

從DecorView向上滾動,你可以看到一個LinearLayout,它包含另一個全屏的背景。它和DecorView的背景是一回事,所以它也是不需要的。唯一可見且肯定存在的背景屬於一個名叫id/tweet_list_container的view

 

去掉桌麵背景:

定 義在你的主題皮膚裏的背景通常是當係統啟動你的應用時用來創建預覽窗口的。千萬不要設置它為空(null),除非你的應用是透明的。相反,設置它為你想要 的顏色或者圖片,或者在onCreate()裏調用getWindow().setBackgroundDrawable(null)來去掉它

 

進一步去掉重繪

用 Photoshop的文檔圖來理解應用是怎麼創建的是很有用的。但是用來去掉小範圍的重繪有點難度。現在我們就必須轉向Tracer for OpenGL。同樣在ADT(或者monitor)中打開它的視圖,點擊工具欄的箭頭圖標,輸入你應用的包名和你主要的Activity的名字,然後選擇 一個目的文件,點擊Trace。

 

一句建議:

OpenGL traces抓取的數據量很大。為了讓數據量較小,同時也利於更快速抓取。請去掉“all the Data Collection Options”選項。

 

Activity名字:

在應用啟動時可以通過logcat獲得包名和Activity名字。這就是為什麼我可以知道在Tracer for OpenGL輸入這些名字。

 

當啟動並運行這個應用時,打開前兩個選項:

  • Collect Framebuffer contents on eglSwapBuffers()

  • Collect Framebuffer contents on glDraw*()

 

第一個選項可以方便的快速找到你感興趣的幀,第二個選項可以讓我們看到每一幀是如何通過一步步繪圖命令建立起來的。第二個選項就是解決重繪的關鍵。

隨著這兩個選項的開啟,我開始滾動屏幕。抓取每一幀需要很長時間(也許要30秒),所以我推薦你可以先簡單的下載我抓取的trace文件。你可以通過Tracer for OpenGL工具欄的第一個按鈕打開這個文件。

trace 文件一旦加載完成,你就可以看到每一幀發生給GPU的每一個GL命令。如果你下載了我的文件,你跳到第21幀。當一幀被選中後,你就可以看到Frame Summary選項卡中呈現的模樣。此外,你還可以點擊高亮為藍色的drawing命令,這樣你就可以在Details選項卡中獲得當前幀的狀態細節。

相繼的點擊前三個繪圖命令,你就可以看到在PhotoShop裏麵已經得到鑒定的問題:全屏的背景被畫了三次。

通過深入研究這個trace文件,我們可以找到更多優化的地方。當去畫一個消息內容條目時,ImageView被用來畫頭像。這個ImageView先畫了一個背景然後再畫頭像:

如果你看得再仔細點你就會注意到背景隻是用來作為圖片的邊框。這就意味著在位於頭像的黑色方塊產生了重繪。那塊9格圖(9-patch)完全被頭像覆蓋了。

解決這個問題的有一個很簡單的方法就是讓這塊9格圖設為透明。Android的2D渲染引擎已經在9格圖上做了優化。這個簡單的方法就可以去掉重繪。

有趣的是,同樣的問題也發生在內嵌的媒體內容上。頭像很小所以它們的重繪不是個大問題。但內嵌的媒體內容卻可以占據屏幕的大片區域,這個問題就嚴重了。可以用同樣的方法去解決它。

未來的優化:

我希望Android的2D渲染流水線能夠自動的檢測和修正重繪。我們已經有了一些想法但還不能做出承諾。

扁平化View的層級

現在重繪已經基本考慮過了。讓我們重新回到Hierarchy Viewer吧。通過研究這棵UI樹,我們可以盡量去鑒別哪些View不是必須的。去掉View,特別是去掉ViewGroups,不僅可以提供幀率,也可以節省內存,加快啟動時間等等。

看一眼Falcon Pro的View的層級樹就可以發現一些ViewGroups是在同一個子節點上。ViewGroups通常不是必須的,也很容易去掉。下麵這個截圖顯示至少有兩個節點是可以去掉的。

也 有一些冗餘的View可以去掉。比如每一個消息條目都包一個叫做id/listElementBottom的RelativeLayout。這個布局包含 了作者的名字,推特消息,已經發布了多長時間和一個圖標。名字和消息用了兩個不同的TextView,其實隻需要一個TextView用不同的風格來顯示 就行了。時間和圖標用了一個TextView和一個ImageView,其實兩者可以用一個TextView,然後用可視化綁定到TextView上。

左邊滑動的界麵用了若幹不同的LinearLayout+TextView+ImageView來顯示標簽和圖標。他們都可以通過一個TextView來代替。

如何扁平化你的界麵:

我在2009年的Google I/O大會上做了一篇題為優化你的UI的演講,裏麵介紹了這其中的技術細節。

關於輸入事件?

還記得我們在看systrace時找到一段處理很慢的觸摸事件?現在可以看看這個問題。理解這個問題最佳的工具就是traceview。

traceview 是Dalvik性能解析工具,它可以測量一個應用在每個方法調用上花費的事件。在ADT或者monitor裏打開DDMS,在設備選項卡裏選擇你應用所在 的進程,然後點擊“start method profiling”按鈕(三個箭頭和一個紅色的圈),你就可以使用traceview。

當啟動了traceview後,我滾動應用的界麵,然後點擊那個按鈕結束跟蹤。你也可以下載我的跟蹤文件。結果如下圖所示。

點擊條目21:ViewRootImpl.draw(),高亮它所花的時間。表的最後一列表明這個方法的和在它的子類裏平均的調用時間。如果你仔細看看高亮的時間軸,你可以注意到幀與幀之間的差距。

用 一個簡單的方法來查看差距裏麵到底發生了什麼,可以放大他們開始的階段,然後點擊你找到的紅色的塊。你可以跟著調用鏈來找到你能認出的方法。在我的例子 裏,我跟蹤了一個大概占用了0.5毫秒的Pattern.compileImpl方法,一直到跟DBListAdapter.bindView。

很 顯然這個應用將同一個正則表達式編譯了好幾次,每一次滾動屏幕都伴隨著一個條目的綁定。TraceView顯示bindView平均占用了38毫秒,而其 中56%的時間花在了解析HTML文本上。似乎可以將這個步驟放在後台運行而不去阻塞UI線程,而正則表達式不應該每次都需要重新編譯。

現在輪到你了!

我保留了最後一個跟蹤文件作為測驗。這個應用有兩個滑動的菜單,可以左右滑動時間軸。Show GPU overdraws高亮了滑動時大量的繪圖。我用Tracer for OpenGL抓取了滑動時的若幹幀。下載我的trace文件,然後看看你是否能找到重繪的原因(去看第34號幀)。

提示:

應 用應該調用View.setLayerType()來使用硬件圖層(hardware layer)來簡化繪圖。大量的背景可以使用9格圖來做優化。裁剪也很有效。最後,也許可以將一個顏色過濾器(colofilter)設置在畫筆 (paint)上,然後傳給setLayerType(),這樣可以幫助去掉最後一個繪圖命令。

我向你們展示了大量可以優化你們應用的工具。我其實還可以花費大量的時間來描述用這些工具處理這些問題的技術方法,但這樣文章就會變成長篇大論。你們可以去參考Android開發者的的官方文檔和所有Google I/O上Android的演講(ppt和視頻都是免費可取得)。

英文原文:Android Performance Case Study  編譯:ImportNew - 孫立

譯文地址:  https://www.importnew.com/4065.html


最後更新:2017-04-03 07:57:08

  上一篇:go Ecshop如何去掉頁麵標題及版權信息
  下一篇:go 解決PHP中使用header輸出頭報錯問題