片頭聲明: 1、本片是據Romain Guy劇本編寫Android Performance Case Study衍生的電影,某些部分可能由于個人英語水平有限及理解原因,可能有別于原作者的原意。如有發現,請指正。以利于我們共同學習,共同進步。 2、本片是繼Android性能優化案例研究(上) by孫立出的下版。狗尾續貂,望大家海涵。 各位看官,下面就接上部開播: 進一步的減少overdraw: 【Activity名字:當你打開應用后,logcat將顯示包名和Activity名字。根據這個你就可以曉得在Tracer for OpenGL中要輸入什么東東了! |
當應用已經打開并運行,使可用前兩個選項: * Collection Framebuffer contents on eglSwapBuffers() * Collection Framebuffer contents on glDraw*() 第一個選項對于快速找到你感興趣的frame十分有用,然而第二個選項則允許我們通過繪制命令查看每個frame生成使用的命令。第二個選項是解決overdraw 問題的關鍵所在。 ![]() 開啟這兩個選項之后,我開始滑動主界面的時間軸。它將花費相當長的時間來獲取每一個frame(不出意外的話,需要30秒)。因此我建議你下載我獲取的文件(http://goo.gl/yPjB5)。你可以在Tracer for OpenGL中點擊第一個按鈕打開這個文件。 加載完畢后,視圖展示你每一個發送到GPU的GL命令為一個frame快照。如果你下載我的文件。跳到21Frame處。當一個frame被選中之后,你可以在Frame Sunmmary tab 頁中查看它是什么樣子的。另外,你可以點擊繪制命令,將其藍色高亮,在Details tab頁中查看這個frame的當前狀態。 【總結:GL 命令被通過View分組。他們重新創建你在HierarchyViewer或者xml Layout文件夾可以看到的樹。這使它很容易就能理解什么View產生了一個什么樣的特定操作! ![]() 通過連續點擊前三個繪制命令,你可以看出已經在Photoshop中確定的問題,一個全屏的背景被繪制了三次。 我們通過查看下載的Tracer,可以發現更多可以優化的地方。當一個tweet(listitem)被繪制,一個ImageView被用于繪制頭像(原文是:avatar)。ImageView第一次繪制了一個背景然后繪制頭像(avatar)自身。 ![]() 如果你仔細看,你將會發現背景只是作為圖片的一個邊界。這意味著,這意味著在頭像圖片中間的這黑色部分被 overdraw了。這片9path已經被頭像圖片(avatar)完全覆蓋了。 ![]() ![]()
|
有趣的是,同樣的問題也出現在了內聯媒體(inline media)中。頭像圖片(Avatars)圖片十分小,所以他們overdraw不會導致大的消耗。但是內嵌媒體(inline media)可占據屏幕相當大的一部分區域。問題的修復與上面的方法一致。 ![]() ![]() 【深度優化:我更希望Android的2d渲染管道有能力自動正確的為你overdraw。我們已經有了些想法,但是我還不能做出任何關于這方面的承諾。正像加入GPU優化,這只能作用于不透明的元素! 扁平化視圖( Flattening the view hierarchy):好的,現在我們已經對overdraw(大部分都是)關心過了,現在讓我們回過頭來再看看Hierarchy viewer。通過檢查圖 像樹,我們能試圖確定不需要的Views。移除Views特別是ViewGroup,這不僅可以提高幀速率,而且還可以減少內存消耗 ,啟動時間等等。 快速查看Falcon Pro視圖層級,你可以確定有幾個ViewGroup和一個單獨的子視圖。這些ViewGroup一般都是不需要并且 很容易移除掉。下面展示的圖片中至少最后面的兩個節點是可以移除的。 ![]() 也有一些另外的視圖可以從這個樹中移除。比如,每一個tweet都包含了一個命名為listElementBottom的RelativeLayout。這個Layout包含了作者的名字,他的Twitter,從tweet發送出來之后已經過去的時間。名字和handle是 兩個分離的TextView代替了單獨一個TextView加入多種類型不同的spans,時間和icon使用一個TextView和一個 ImageView可以被結合稱一個單獨的TextView,查看TextView's compound drawables 左邊的菜單使用了多個LinearLayout+TextView+ImageView的組合來展示標簽和圖標。其實每個可以使用單獨一個TextView就可以代替了。 |
關于“輸入”事件:記不記得當我們查看systrace并且發現,當處理觸摸事件的時候會有些卡頓?現在,是時候來追蹤這個 問題了,traceview是我們去明晰系統正在做什么的最好工具。 Traceview是一個記錄應用在調用某個方法花費多少時間的虛擬機分析器?梢栽贏DT的DDMS視圖或者monitor中啟動他,在Devices tab中選擇你應用進程,然后點擊Start method profiling按鈕。紅色環繞的三個箭頭。 在開啟追蹤之后,我來回滑動主時間軸,并且重新點擊按鈕來結束trace。你也可以下載我的追蹤記錄: 結果如下圖截屏所示。 ![]() 點擊第#21,ViewRootImpl.draw().高亮繪制時間。表格中最后一行給你一個建議關于如何平均花在這個方法和起子方法的時間。如你使用高亮來仔細看這時間軸,你將會注意到在連續frames之間的間隙。 一個最簡單找出這些在這些間隙中到底發生了什么方法就是(選擇他們其中一個的開始部分進行放大,然后點擊你能發 現的最大色塊)你追蹤父子鏈(parent chain)直到找到你可以辨識出來的東西。在我的例子中,我追蹤一個花費了平 均時間的0.5ms的叫Patter.compileImpl,所有都指向了DBListAdapter.bindView。 顯然每次一旦有新的item被綁定或者滑動主時間軸時,應用都會重新一遍一遍的編譯這相同的正則表達式。Traceview展示了綁定一個View平均花費38ms并且有56%的時間花費在解析HTML文本上。這看起來某些東西可以被可以在后臺實現而不 是阻塞UI線程,正則表達式不應該每次都要重新編譯。 接下來看你的了! 我留下最后一個trace作為練習。這個應用在兩邊有兩個菜單,可以通過敲擊時間線的左邊或者右邊來顯示。(在顯示這 菜單時GPU overdraws 高亮了過度繪制的部分,我已經使用Tracer for OpenGL來獲取了這個問題的幾幀。下載我的trace然后看看你是否能找到什么導致了overdraw(作者提示go to frame #34)。 提示就不翻譯了: Hints: the application should use hardware layers by calling View.setLayerType() to simplify drawing. There are also extraneous backgrounds that can be optimized away with clever use of 9-patches. Clipping could also be very helpful. Finally, maybe a ColorFilter set on a Paint passed to setLayerType() could help remove the last drawing command.
【后語:最好使用Monitor,鄙人在使用ADT的時候老出錯。 |