一個優秀的應用不只僅是要有吸引人的功能和交互,同時在性能上也有很高的要求。運行Android系統的手機,雖然配置在不斷的提高,但仍舊沒法和PC相比,沒法作到PC那樣擁有超大的內存以及高性能的CPU,所以在開發Android應用程序時也不可能無限制的使用CPU和內存,若是對CPU和內存使用不當也會形成應用的卡頓和內存溢出等問題。所以,應用的性能優化對於開發人員有着更高的要求。Android性能優化分爲不少種,比較經常使用的有繪製優化、內存優化、耗電優化和穩定性優化等,這個系列咱們就來學習性能優化中的繪製優化。html
Android繪製View有三個主要的步驟,分別是measure、layout和draw。關於它們的原理請查看個人文章Android View體系(七)從源碼解析View的measure流程和Android View體系(八)從源碼解析View的layout和draw流程,這裏就不在贅述。measure、layout和draw方法主要是運行在系統的應用框架層,而真正將數據渲染到屏幕上的則是系統Nativie層的SurfaceFlinger服務來完成的。python
繪製過程主要是由CPU 來進行Measure、Layout、Record、Execute的數據計算工做,GPU負責柵格化、渲染。CPU和GPU是經過圖形驅動層來進行鏈接的。圖形驅動層維護了一個隊列,CPU將display list添加到該隊列中,這樣GPU就能夠從這個隊列中取出數據進行繪製。android
FPS(Frames Per Second)這個名詞我想不少同窗都知道,它是指畫面每秒傳輸幀數,通俗來說就是指動畫或視頻的畫面數,最簡單的舉例就是咱們玩遊戲時,若是畫面在60fps則不會感受到卡頓,若是低於60fps,好比50fps則會感受到卡頓,你就能夠考慮要換顯卡或者採起其餘一些措施了。
要想畫面保持在60fps,則須要每一個繪製時長在16ms之內,以下圖所示。性能優化
Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染, 若是每次渲染都成功,這樣就可以達到流暢的畫面所須要的60fps,那什麼是VSYNC呢?VSYNC是Vertical Synchronization(垂直同步)的縮寫,是一種定時中斷,一旦收到VSYNC信號,CPU就開始處理各幀數據。
若是某個操做要花費24ms,這樣系統在獲得VSYNC信號時沒法進行正常的渲染,會發生丟幀。用戶會在32ms中看到同一幀的畫面,以下圖所示。框架
產生卡頓緣由有不少,主要有如下幾點:ide
爲了解決上述的問題,除了咱們要在寫代碼時要注意外,也能夠藉助一些工具來分析和解決卡頓問題。函數
Profile GPU Rendering是Android 4.1系統提供的開發輔助功能,咱們能夠在開發者選項中打開這一功能,以下圖所示。工具
打開Profile GPU Rendering_副本_副本.png
咱們點擊Profile GPU Rendering選項並選擇On screen as bars即開啓Profile GPU Rendering功能。接着屏幕會顯示出彩色的柱狀圖,以下所示。佈局
上面的彩色的圖的橫軸表明時間,縱軸表示某一幀的耗時。綠色的橫線爲警惕線,超過這條線則意味着時長超過了16m,儘可能要保證垂直的彩色柱狀圖保持在綠線下面。這些垂直的彩色柱狀圖表明着一幀,不一樣顏色的彩色柱狀圖表明不一樣的含義:性能
在Android 6.0中,有更多的顏色被加了進來,以下圖所示:
下面來分別介紹它們的含義:
Profile GPU Rendering能夠找到渲染有問題的界面,可是想要修復的話,只依賴Profile GPU Rendering是不夠的,能夠用另外一個工具Hierarchy Viewer來查看佈局層次和每一個View所花的時間,這個工具會在下一篇文章進行介紹。
Systrace是Android4.1中新增的性能數據採樣和分析工具。它可幫助開發者收集Android關鍵子系統(SurfaceFlinger、WindowManagerService等Framework部分關鍵模塊、服務,View體系系統等)的運行信息。Systrace的功能包括跟蹤系統的I/O操做、內核工做隊列、CPU負載以及Android各個子系統的運行情況等。對於UI顯示性能,好比動畫播放不流暢、渲染卡頓等問題提供了分析數據。
Systrace跟蹤的設備要在Android4.1版本以上,對於Android4.3版本以前和4.3版本以後使用上有點區別,如今也不多有人用Android4.3以前的版本,所以這裏只講Android4.3版本的使用方法。Systrace能夠在DDMS上使用,可使用命令行來使用,也能夠在代碼中進行跟蹤。接下來分別來介紹這三種方式。
在DDMS中使用Systrace
1.首先咱們要打開Android Studio的Tool中的Android Device Monitor,並鏈接手機。
2.點擊Systrace按鈕進入抓取設置界面,以下圖所示。
抓取設置界面能夠設置跟蹤的時間,以及trace文件輸出的地址等內容。以下圖所示。QQ截圖20170311224620_副本.png
3.設置完成後,咱們就來操做的跟蹤的過程。跟蹤時間結束後,生成trace.html文件。
4.用Chrome打開trace.html文件進行分析。分析的方法,後文會講到。
用命令行使用Systrace
Android 提供一個python腳本文件 systrace.py,它位於Android SDK 目錄 /tools/systrace 中,咱們能夠執行如下命令來使用Systrace:
$ cd android-sdk/platform-tools/systrace $ python systrace.py --time=10 -o newtrace.html sched gfx view wm
在代碼中使用Systrace
Systrace並不會追蹤應用的全部工做,在Android4.3及以上版本的代碼中,可使用Trace類對應用中的具體活動進行追蹤。
Android源碼中也引用了Trace類,好比RecyclerView:
... private final Runnable mUpdateChildViewsRunnable = new Runnable() { public void run() { if (!mFirstLayoutComplete) { return; } if (mDataSetHasChangedAfterLayout) { TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG); dispatchLayout(); TraceCompat.endSection(); } else if (mAdapterHelper.hasPendingUpdates()) { TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG); eatRequestLayout(); mAdapterHelper.preProcess(); if (!mLayoutRequestEaten) { rebindUpdatedViewHolders(); } resumeRequestLayout(true); TraceCompat.endSection(); } } }; ...
TraceCompat類對Trace類進行了封裝,只會在Android4.3及以上版本纔會使用Trace類,其中beginSection方法和endSection方法之間的代碼會被追蹤,endSection方法會只會結束最近的beginSection方法,所以要保證beginSection方法和endSection方法的調用次數要相同。
經過前面的方法生成的trace.html須要用Chrome打開,打開後效果以下圖所示。
咱們可使用W鍵和S鍵進行放大和縮小,A鍵和D鍵進行左右移動。
Alert區域
首先來看Alert區域,這一區域會標記處性能有問題的點,單擊歎號圖標就能夠查看某一個Alert的問題描述,以下所示。
這個Alert指出了View在Measure/Layout時耗費了大量的時間,致使出現jank(同一幀畫了屢次)。給出的建議是避免在動畫播放期間控制佈局。
CPU區域
接下來咱們來查看CPU區域,每一行表明一個CPU核心和它執行任務的時間片,放大後會看到每一個色塊表明一個執行的進程,色塊的長度表明其執行時間,以下圖所示。
圖中CPU 0主要執行adbb線程和InputReader線程,CPU 2主要執行了surfaceflinger線程和ordinatorlayout進程中的RenderThread線程,咱們點擊RenderThread色塊,會給出RenderThread的相關信息,以下圖所示。
圖中給出了當前色塊所運行的線程和進程、開啓時間和持續時間等信息。
Systrace會給出應用中的Frames分析,每一幀就是一個F圓圈,F圓圈有三種顏色,其中綠色表示Frame渲染流暢,黃色和紅色則表明渲染時間超過了16.6ms,其中紅的更嚴重些。咱們點擊紅色F圓圈,會給出該Frame的信息,以下圖所示。
從圖中能夠看出,Frame給出了問題提示:Scheduling delay(調度延遲),當一幀繪製時間超過19ms會觸發該提示,更況且這一幀已經有將近40ms了。致使這一問題產生的緣由主要是線程在繪製時,在很長一段時間都沒有分配到CPU時間片,所以沒法繼續進行繪製。按m鍵來高亮該時間段,咱們來查看CPU的狀況,以下圖所示。
能夠看出這個時間段中兩個CPU都在滿負荷運行。至於具體是什麼讓CPU繁忙,則須要使用Traceview來進行分析。
Alerts整體分析
點開最右邊的Alerts按鈕會給出Alert的整體分析,以下圖所示。QQ截圖20170312150637.png
Alerts會給出Alert類型,以及出現的次數。有了這些整體的分析,方便開發者對該時間段的繪製性能有一個總體的大概瞭解,便於進行下一步分析。
因爲Systrace 是以系統的角度返回一些信息,只能爲咱們提供一個概覽,它的深度是有限的,咱們能夠用它來進行粗略的檢查,以便了解大概的狀況,可是若是要分析更詳細的,好比要找到是什麼讓CPU繁忙,某些方法的調用次數等,則還要藉助另外一個工具:Traceview。
TraceView是Android SDK中自帶的數據採集和分析工具。通常來講,經過TraceView咱們能夠獲得如下兩種數據:
要分析Traceview,則首先要獲得一個trace文件,trace文件的獲取有兩種方式,分別是在DDMS中使用和在代碼中加入調試語句,下面分別對這兩種方式進行介紹。
DDMS中使用
1.首先咱們要打開Android Studio的Tool中的Android Device Monitor,並鏈接手機。
2.選擇相應的進程,並單擊Start Method Profiling按鈕。
3.對應用中須要監控的點進行操做。
4.單擊Stop Method Profiling按鈕,會自動跳到TraceView視圖。
代碼中加入調試語句
若是開發中出現很差復現的問題,則須要在代碼中添加TraceView監控語句,代碼以下所示。
Debug.startMethodTracing();
...
Debug.stopMethodTracing();
在開始監控的地方調用startMethodTracing方法,在須要結束監控的地方調用stopMethodTracing方法。系統會在SD卡中生成trace文件,將trace文件導出並用SDK中的Traceview打開便可。固然不要忘了在manifest中加入 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
權限。
爲了分析Traceview,咱們來舉一個簡單的例子來生成trace文件,這裏採用第二種方式:代碼中加入調試語句。代碼以下所示。
public class CoordinatorLayoutActivity extends AppCompatActivity { private ViewPager mViewPager; private TabLayout mTabLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab_layout); Debug.startMethodTracing("test");//1 initView(); ... } private void initView() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override protected void onStop() { super.onStop(); Debug.stopMethodTracing(); } }
在註釋1處調用了startMethodTracing方法開始監控,其中test是生成的trace文件的名稱。在initView中咱們特地調用sleep方法來作耗時操做。在onStop方法中咱們調用了stopMethodTracing方法結束監控。這時會在SD卡根目錄生成test.trace文件,咱們將該文件導出到桌面,用Traceview來分析test.trace文件,咱們在cmd中執行以下語句。
咱們進入traceview所在的目錄(直接將traceview.bat拖入到cmd中),並執行上圖的traceview語句後會彈出Traceview視圖,它分爲兩部分,分別是時間片面板和分析面板,咱們先來看時間片面板,以下圖所示。
其中x軸表明時間的消耗,單位爲ms,y軸表明各個線程。通常會查看色塊的長度,明顯比較長的方法重點去關注,具體的分析還得看分析面板,以下圖所示。
每一列數據的表明的含義以下表所示。
列名 | 含義 |
---|---|
Name | 該線程運行過程當中調用的函數名 |
Incl Cpu Time% | 某個方法包括其內部調用的方法所佔用CPU時間百分比 |
Excl Cpu Time% | 某個方法不包括其內部調用的方法所佔用CPU時間百分比 |
Incl Real Time% | 某個方法包括其內部調用的方法所佔用真實時間百分比 |
Excl Real Time% | 某個方法不包括其內部調用的方法所佔用真實時間百分比 |
Calls + Recur Calls / Total | 某個方法次數+遞歸調用次數 |
Cpu Time / Call | 該方法平均佔用CPU時間 |
Cpu Time / Call | 該方法平均佔用真實時間 |
Incl Cpu Time | 某個方法包括其內部調用的方法所佔用CPU時間 |
Excl Cpu Time | 某個方法不包括其內部調用的方法所佔用CPU時間 |
Incl Real Time | 某個方法包括其內部調用的方法所佔用真實時間 |
Excl Real Time | 某個方法不包括其內部調用的方法所佔用真實時間 |
由於咱們用sleep方法來進行耗時操做,因此這裏咱們能夠單擊Incl Real Time來進行降序排列。其中有不少系統調用的方法,咱們來進行一一過濾。最終咱們發現了CoordinatorLayoutActivity的initView方法Incl Real Time的時間爲1000.493ms,這顯然有問題,以下圖所示。從圖中咱們能夠看出是調用sleep方法致使的耗時。關於Traceview還有不少種分析狀況,就須要你們在平時進行積累了。好了關於繪製性能分析,就講到這,若是以爲不過癮,本系列的後續文章還有大波的內容會持續向你砸來。