安卓性能優化(響應優化)

安卓app響應速度或使用流暢度是衡量性能的一個指標。如果一個應用用戶啓動應用時緩慢、使用時卡頓、甚至出現ANR那是很糟糕的體驗。
通常,當應用無法響應用戶輸入時,系統即會顯示 ANR。例如,如果應用在界面線程中執行了某些 I/O 操作(文件的讀寫等),導致系統無法處理傳入的用戶輸入事件。或者,應用在界面線程中花費太多時間構建複雜的內存結構或計算遊戲的下一個走法。確保高效的計算始終至關重要,但即使最高效的代碼仍然需要時間來運行。
在 Android 中,應用響應性由 Activity 管理器和窗口管理器系統服務監控。當 Android 檢測到以下某一項條件時,便會針對特定應用顯示 ANR 對話框:

在 5 秒內對輸入事件(例如按鍵或屏幕輕觸事件)沒有響應。
BroadcastReceiver 在 10 秒後尚未執行完畢。

app啓動和流暢度優化

1,app啓動流程

安卓系統啓動後,會有一個Zygote進程,系統創建應用都是從這個進程中fork出來的,Java層入口可以查看源碼中的ActivityThread類的main方法,當啓動app(冷啓動)時,安卓系統會從Zygote進程中fork一個新的進程分配給該應用,之後會依次創建和初始化Application類、創建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設置給MainActivity以及配置Activity層級上的一些屬性、再inflate佈局、當onCreate/onStart/onResume方法都走完了後最後才進行contentView的measure/layout/draw顯示在界面上( 這些在ActivityThread\ActivityManagerService\BIND機制 等配合着看下源碼就可以看到,這裏可以看到源碼中windowmanager.addView(decorview)是在performResumeActivity之後執行,所以View的繪製是在onResume之後執行的),所以直到這裏,應用的第一次啓動纔算完成,這時候我們看到的界面也就是所說的第一幀。所以,總結一下,應用的啓動流程如下:

Application的構造器方法——>attachBaseContext()——>onCreate()——>Activity的構造方法——>onCreate()——>配置主題中背景等屬性——>onStart()——>onResume()——>測量佈局繪製顯示在界面上。

應用在冷啓動之前,要執行三個任務:

——加載啓動App;

——創建App的進程;

——App啓動之後立即展示出一個空白的Window;

而這三個任務執行完畢之後會馬上執行以下任務:

——創建App對象;

——啓動Main Thread;

——創建啓動的Activity對象;

——加載View;

——佈置屏幕;

——進行第一次繪製;

而一旦App進程完成了第一次繪製,系統進程就會用MainActivity替換已經展示的Background Window(可以添加logo增加用戶體驗),此時用戶就可以使用App了。

2,使用Systrace查看問題

systrace的一個優勢是可以整個手機全局的檢查各個應用的進程。

2.1,生成trace文件

打開Android Device Monitor(Windows下雙擊SDK\tools\monitor.bat),
點擊下圖紅圈

在這裏插入圖片描述
彈出如下圖:
紅色圈圈分別代表trace的時間(通常設置默認值5秒,並在5秒內重現問題,時間太短會導致問題重現時沒有被抓到,時間太長會導致JavaHeap不夠而無法保存,可能會出現無法解析的情況,因此在能抓到問題點的情況下,時間越小越好)、trace的大小(同樣的,太小會導致信息丟失,時間太長會導致Java Heap不夠而無法保存,建議使用默認的2048)、trace哪些TAG

在這裏插入圖片描述
點擊OK後,執行啓動app的操作。

2.2,使用Chrome打開chrome://tracing/,Load trace文件和分析

比如下圖:
左側是對應的應用ID,每個應用展開裏面有Heap size、Frames、UIThread等item。
其中比較重要的兩個,

Frames:
在每個App進程,都有一個Frames行,正常情況以綠色的圓點表示。當圓點顏色爲黃色或者紅色時,意味着這一幀超過16.6ms(即發現丟幀),這時需要通過放大那一幀進一步分析問題。對於Android
5.0(API level 21)或者更高的設備,該問題主要聚焦在UI Thread和Render Thread這兩個線程當中。對於更早的版本,則所有工作在UI Thread,沒有Render Thread這一項。

Alerts:
Systrace能自動分析trace中的事件,點擊Alerts裏的每一項,底部有顯示可能的原因和建議該怎麼解決。
比如對於丟幀時,點擊黃色或紅色的Frames圓點便會有相關的提示信息;另外,在systrace的最右側,有一個Alerts tab可以展開,這裏記錄着所有的的警告提示信息。
當我們點擊了Alerts或者點擊右邊的Alerts列表中的任何一點我們可以看到在界面的最底部會相對應的優化提示以及可能會出現優化的視頻教程鏈接。
在這裏插入圖片描述
如上圖,可以看到左側對應的進程名右側,MethodName爲bindApplication的這個方法執行了792.154ms,源碼中搜索bindApplication(在ActivityThread中,同樣可以找到上圖中activityStart、activityResume主要執行Activity的onCreate\onStart\onResume等方法),發現裏面有執行Application的onCreate方法,這裏就要檢查是否在application的onCreate方法裏初始化的東西太多,非必要的可以考慮子線程或者延後初始化。
同上面app啓動流程,接下來可以看到bindApplication後面activityStart\activityResume\traversal等,可以鼠標選中+M鍵快速標記當前的方法下使用的時間(其它快捷鍵有W放大\S縮小\A左移\D右移 等,可以自己點擊試試),如上圖顯示的bindApplication耗時792.154ms。如果發現耗時比較長的,可以到代碼中查找對應的方法裏是否執行了耗時之類的操作或者對應時間上的CPU、內存等的使用情況。

該文件主要是發現問題(右側有個Alerts,提示哪些地方有可能有問題),一般和競品生成的文件做對比,可以發現時間主要用在哪一塊,具體問題點一般還要到具體的代碼或者使用TraceView去檢查定位到具體的方法,進而修改。

3,使用TraceView查看耗時方法

如下圖:
選中進程,點擊紅圈的圖標start method profiling,重現問題,再次點擊紅圈圖標stop method profiling。

在這裏插入圖片描述
生成如下圖xxx.trace文件:

在這裏插入圖片描述
各個參數的含義:

在這裏插入圖片描述
如上圖所示,可以搜索具體的包名或者按照某一項的參數遞增或遞減排序,比如上圖,獲得方法執行的次數和執行總的時間。點擊方法可以看到裏面的各個調用方法時間分配情況,最上面還有對應的方法執行時間刻度和所在線程(滾動鼠標可以放大和縮小),進而定位到具體的方法,修改解決問題。

4,項目中一些具體操作:

1,如果想在systrace抓取的trace文件裏有自己的方法,可以使用Trace.beginSection(“sectionName”) +Trace.endSection()(這個需要在生成trace文件時選中進程,否則不會被抓取到),如果是系統Rom可以使用隱藏API Trace.traceBegin(long traceTag, String methodName)+traceEnd(long traceTag)。

2,使用“設置 => 開發者選項 => 調試 GPU 過度繪製 => 顯示過渡繪製區域”,根據屏幕顯示的不同顏色來區分是存在過度繪製,從而排查該界面的 xml 文件,去除不必要的嵌套和background。比如使用ViewStub\merge等。當然也可以使用Layout Inspector查看界面佈局情況,進行去除多餘層級和負責佈局的優化。

3,也可以使用Hierarchy View(不過要使用老版本的或者使用開發機,否則在WindowManageService.java的startViewServer返回false,因爲[ro.secure]:[1],[ro.debuggable]: [0],這個可以adb shell getprop | findstr ro.查看),查看各個view的加載時間,並有針對的優化,比如自定義View是否在draw裏面執行了比較重的操作或者實例化了比較多或者大的佔資源的對象。

4,application裏實例化的項儘量子線程、延遲加載或懶加載,將Activity的第一個頁面的內容分頁加載,甚至只加載前兩三項,達到快速加載的目的。當然這個也和系統有關係,所以可以使用一個空的Activity demo查看下啓動的時間,可以在Main log裏搜索到Displayed com.xx.xx.MainActivity: +279ms表示這個空的Activity啓動耗時,以這個作爲這個手機的參考參數。

5,對於短時間大量執行的方法,非必須的話就不使用synchronized,因爲這個也會增加執行時間和資源開銷。

6,避免短時間創建大量臨時變量導致GC,出現卡頓,可以考慮使用pool。

7,避免主線程執行比較重的磁盤 I/O 操作或者比較頻繁的耗時操作(這甚至就會直接阻塞應用的執行),對於這類必須要執行的耗時操作,可以放到子線程中執行,並且儘可能減少執行時間(比如對數據庫的批量操作)。

8,避免大量的多重循環等等。

好了好了,目前就記得這些了,等後面有新的再加上來。
希望疫情早點結束,大家都健健康康的,一切恢復正常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章