Android繪製優化(一)繪製性能分析

来源:https://www.cnblogs.com/ganchuanpu/archive/2018/07/16/9316085.html
-Advertisement-
Play Games

前言 一個優秀的應用不僅僅是要有吸引人的功能和交互,同時在性能上也有很高的要求。運行Android系統的手機,雖然配置在不斷的提升,但仍舊無法和PC相比,無法做到PC那樣擁有超大的記憶體以及高性能的CPU,因此在開發Android應用程式時也不可能無限制的使用CPU和記憶體,如果對CPU和記憶體使用不當也 ...


前言

一個優秀的應用不僅僅是要有吸引人的功能和交互,同時在性能上也有很高的要求。運行Android系統的手機,雖然配置在不斷的提升,但仍舊無法和PC相比,無法做到PC那樣擁有超大的記憶體以及高性能的CPU,因此在開發Android應用程式時也不可能無限制的使用CPU和記憶體,如果對CPU和記憶體使用不當也會造成應用的卡頓和記憶體溢出等問題。因此,應用的性能優化對於開發人員有著更高的要求。Android性能優化分為很多種,比較常用的有繪製優化、記憶體優化、耗電優化和穩定性優化等,這個系列我們就來學習性能優化中的繪製優化。

1.繪製原理

Android繪製View有三個主要的步驟,分別是measure、layout和draw。關於它們的原理請查看我的文章Android View體系(七)從源碼解析View的measure流程Android View體系(八)從源碼解析View的layout和draw流程,這裡就不在贅述。measure、layout和draw方法主要是運行在系統的應用框架層,而真正將數據渲染到屏幕上的則是系統Nativie層的SurfaceFlinger服務來完成的。

繪製過程主要是由CPU 來進行Measure、Layout、Record、Execute的數據計算工作,GPU負責柵格化、渲染。CPU和GPU是通過圖形驅動層來進行連接的。圖形驅動層維護了一個隊列,CPU將display list添加到該隊列中,這樣GPU就可以從這個隊列中取出數據進行繪製。

1.1 渲染時間線

FPS(Frames Per Second)這個名詞我想很多同學都知道,它是指畫面每秒傳輸幀數,通俗來講就是指動畫或視頻的畫面數,最簡單的舉例就是我們玩游戲時,如果畫面在60fps則不會感覺到卡頓,如果低於60fps,比如50fps則會感覺到卡頓,你就可以考慮要換顯卡或者採取其他一些措施了。
要想畫面保持在60fps,則需要每個繪製時長在16ms以內,如下圖所示。

04080416_dgEb.png

Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染, 如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,那什麼是VSYNC呢?VSYNC是Vertical Synchronization(垂直同步)的縮寫,是一種定時中斷,一旦收到VSYNC信號,CPU就開始處理各幀數據。
如果某個操作要花費24ms,這樣系統在得到VSYNC信號時無法進行正常的渲染,會發生丟幀。用戶會在32ms中看到同一幀的畫面,如下圖所示。

04080416_cWwX.png

產生卡頓原因有很多,主要有以下幾點:

  • 佈局Layout過於複雜,無法在16ms內完成渲染。
  • 同一時間動畫執行的次數過多,導致CPU或GPU負載過重。
  • View過度繪製,導致某些像素在同一幀時間內被繪製多次。
  • UI線程中做了稍微耗時的操作。

為瞭解決上述的問題,除了我們要在寫代碼時要註意外,也可以藉助一些工具來分析和解決卡頓問題。

2.Profile GPU Rendering

Profile GPU Rendering是Android 4.1系統提供的開發輔助功能,我們可以在開發者選項中打開這一功能,如下圖所示。

打開Profile GPU Rendering_副本_副本.png打開Profile GPU Rendering_副本_副本.png
我們點擊Profile GPU Rendering選項並選擇On screen as bars即開啟Profile GPU Rendering功能。接著屏幕會顯示出彩色的柱狀圖,如下所示。

Screenshot_20170308-152841_副本.png
上面的彩色的圖的橫軸代表時間,縱軸表示某一幀的耗時。綠色的橫線為警戒線,超過這條線則意味著時長超過了16m,儘量要保證垂直的彩色柱狀圖保持在綠線下麵。這些垂直的彩色柱狀圖代表著一幀,不同顏色的彩色柱狀圖代表不同的含義:

  • 橙色代表處理的時間,是CPU告訴GPU渲染一幀的地方,這是一個阻塞調用,因為CPU會一直等待GPU發出接到命令的回覆,如果橙色柱狀圖很高,則表明GPU很繁忙。
  • 紅色代表執行的時間,這部分是Android進行2D渲染 Display List的時間。如果紅色柱狀圖很高,可能是由重新提交了視圖而導致的。還有複雜的自定義View也會導致紅的柱狀圖變高。
  • 藍色代表測量繪製的時間,也就是需要多長時間去創建和更新DisplayList。如果藍色柱狀圖很高,可能是需要重新繪製,或者View的onDraw方法處理事情太多。

在Android 6.0中,有更多的顏色被加了進來,如下圖所示:
QQ截圖20170320133358.png

下麵來分別介紹它們的含義:

  • Swap Buffers:表示處理的時間,和上面講到的橙色一樣。
  • Command Issue:表示執行的時間,和上面講到的紅色一樣。
  • Sync & Upload:表示的是準備當前界面上有待繪製的圖片所耗費的時間,為了減少該段區域的執行時間,我們可以減少屏幕上的圖片數量或者是縮小圖片的大小。
  • Draw:表示測量和繪製視圖列表所需要的時間,和上面講到的藍色一樣。
  • Measure/Layout:表示佈局的onMeasure與onLayout所花費的時間,一旦時間過長,就需要仔細檢查自己的佈局是不是存在嚴重的性能問題。
  • Animation:表示計算執行動畫所需要花費的時間,包含的動畫有ObjectAnimator,ViewPropertyAnimator,Transition等。一旦這裡的執行時間過長,就需要檢查是不是使用了非官方的動畫工具或者是檢查動畫執行的過程中是不是觸發了讀寫操作等等。
  • Input Handling:表示系統處理輸入事件所耗費的時間,粗略等於對事件處理方法所執行的時間。一旦執行時間過長,意味著在處理用戶的輸入事件的地方執行了複雜的操作。
  • Misc Time/Vsync Delay:表示在主線程執行了太多的任務,導致UI渲染跟不上VSYNC的信號而出現掉幀的情況。

Profile GPU Rendering可以找到渲染有問題的界面,但是想要修複的話,只依賴Profile GPU Rendering是不夠的,可以用另一個工具Hierarchy Viewer來查看佈局層次和每個View所花的時間,這個工具會在下一篇文章進行介紹。

3.Systrace

Systrace是Android4.1中新增的性能數據採樣和分析工具。它可幫助開發者收集Android關鍵子系統(SurfaceFlinger、WindowManagerService等Framework部分關鍵模塊、服務,View體繫系統等)的運行信息。Systrace的功能包括跟蹤系統的I/O操作、內核工作隊列、CPU負載以及Android各個子系統的運行狀況等。對於UI顯示性能,比如動畫播放不流暢、渲染卡頓等問題提供了分析數據。

3.1 使用Systrace

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_副本.pngQQ截圖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();
            }
        }
    };
    ...
View Code

TraceCompat類對Trace類進行了封裝,只會在Android4.3及以上版本才會使用Trace類,其中beginSection方法和endSection方法之間的代碼會被追蹤,endSection方法會只會結束最近的beginSection方法,因此要保證beginSection方法和endSection方法的調用次數要相同。

3.2 用Chrome分析Systrace

通過前面的方法生成的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.pngQQ截圖20170312150637.png

Alerts會給出Alert類型,以及出現的次數。有了這些總體的分析,方便開發者對該時間段的繪製性能有一個整體的大概瞭解,便於進行下一步分析。
由於Systrace 是以系統的角度返回一些信息,只能為我們提供一個概覽,它的深度是有限的,我們可以用它來進行粗略的檢查,以便瞭解大概的情況,但是如果要分析更詳細的,比如要找到是什麼讓CPU繁忙,某些方法的調用次數等,則還要藉助另一個工具:Traceview。

4.Traceview

TraceView是Android SDK中自帶的數據採集和分析工具。一般來說,通過TraceView我們可以得到以下兩種數據:

  • 單次執行耗時的方法。
  • 執行次數多的方法。

4.1 使用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();
View Code

在開始監控的地方調用startMethodTracing方法,在需要結束監控的地方調用stopMethodTracing方法。系統會在SD卡中生成trace文件,將trace文件導出並用SDK中的Traceview打開即可。當然不要忘了在manifest中加入 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>許可權。

4.2 分析Traceview

為了分析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();
    }
}
View Code

在註釋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還有很多種分析情況,就需要大家在平時進行積累了。
好了關於繪製性能分析,就講到這,如果覺得不過癮,本系列的後續文章還有大波的內容會持續向你砸來。

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、表鎖和行鎖 表鎖和行鎖鎖的粒度不一樣,表鎖鎖住的是一整張表,行鎖鎖住的是表中的一行數據。 InnoDB使用的是行級鎖,MyISAM使用的是表級鎖。 註意:在InnoDB中,例如模糊查詢select * from tb where name like 'lin%'的時候也會鎖住一整張表。 2、共用 ...
  • 有表tb, 如下:id value 1 aa,bb2 aaa,bbb,ccc欲按id,分拆value列, 分拆後結果如下:id value 1 aa1 bb2 aaa2 bbb2 ccc --方法1.使用xml完成SELECT A.id, B.value FROM( SELECT id, [valu ...
  • 筆者在寫上一篇文章Java併發簡介 中腦子裡面同時也閃爍著,程式中有併發問題,那資料庫中也有類似問題嗎? 讓我們一起看一下吧! 事務是將一組讀寫操作組合在一起形成一個邏輯單元。這些操作要麼全部執行成功提交(commit),要麼全部中止失敗(abort,rollback),不會留下一個中間狀態的爛攤子 ...
  • 課時7 開發工具選用Webstorm課時8 Javascript基礎課時9 ES6基礎課時10 Node基礎課時11 React JSX基礎課時12 初識React組件化開發課時13 組件的生命周期課時14 組件間通信課時15 官方組件和文檔一覽課時16 項目的基本結構課時17 如何規範項目代碼課時 ...
  • Apple 終於在 Swift 4 的 Foundation 的模塊中添加了對 JSON 解析的原生支持。 雖然已經有很多第三方類庫實現了 JSON 解析,但是能夠看到這樣一個功能強大、易於使用的官方實現還是不免有些興奮。 值得註意的是,官方的實現方式適用於任何 Encoder/Decoder ,例 ...
  • 面對一些不規範的json,我們的gson解析經常會拋出各種異常導致app崩潰,這裡可以採取一些措施來避免 json異常的處理 我們期望在後臺返回的json異常時,也能解析成功,空值對應的轉換為預設值,如:newsId=0;這裡排除掉後臺開發人員輸出時給你做矯正,還是得靠自己啊 我們寫一個針對int值 ...
  • 前言:oc中枚舉的正確使用,可以增強代碼的可讀性,減少各種“錯誤”,讓代碼更加的規範。下麵先介紹枚舉的用法,最後介紹個人對枚舉的理解,什麼是枚舉,為什麼用枚舉。 一. OC中,枚舉的使用 1. 寫法1格式:關鍵字 enum 枚舉名字{枚舉成員,枚舉成員2}; 使用方法:enum 枚舉名稱 變數名稱 ...
  • 前言 我們知道一個界面的測量和繪製是通過遞歸來完成的,減少佈局的層數就會減少測量和繪製的時間,從而性能就會得到提升。當然這隻是佈局優化的一方面,那麼如何來進行佈局的分析和優化呢?本篇文章會給你一個滿意的答案。 1.佈局優化工具 在講到如何去佈局優化前,我們先來學習兩種佈局優化的工具。 1.1 Hie ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...