Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android性能測試

Android性能測試

編輯:關於Android編程

一直以來Android性能測試一直是Android測試中一個被一部分人遺忘,有被一部分人無可奈何的東西。在絕大部分的創業公司,性能測試基本上都是被遺忘的,因為功能測試和穩定性測試才是重點,而在中等公司中一部分測試人員向對Android進行性能測試,卻無從下手。Android性能測試一直存在測試維度少,測試數據難收集,已收集數據難量化的特點,這些特點又是因為Android手機版本碎片化、硬件多樣化、App功能復雜造成的。

性能測試總的來說,可以分為卡頓ANR測試、流暢度測試、電量測試、流量測試。一個APP為什麼需要性能測試,總的來說就是一些不嚴謹的代碼,在低端機型造成卡頓,對手機上有限電量的浪費,昂貴流量的浪費,造成用戶流失。

一、卡頓ANR測試

卡頓ANR與Android就是天生的朋友,從Android第一天誕生直到現在的8核CPU,Android還是未能擺脫頁面不流暢,卡,死機的诟病,所以個人認為卡頓ANR測試是性能測試最主要的一塊。

卡頓簡單的來說,就是手機沒有及時響應、頁面延遲,出現丟幀的現象,或者點擊無響應。絕大多數的卡頓,稍等片刻系統就會恢復正常,但假如超過5S,就可能會引發手機ANR,造成更高級別的警告。如圖所示:

\

 

1.1 什麼會引發ANR?

在Android裡,應用程序的響應性是由Activity Manager和WindowManager系統服務監視的 。當它監測到以下情況中的一個時,Android就會針對特定的應用程序顯示ANR:

ANR一般有三種類型:

1)KeyDispatchTimeout(5 seconds) --主要類型按鍵或觸摸事件在特定時間內無響應

2)BroadcastTimeout(10 seconds) --BroadcastReceiver在特定時間內無法處理完成

3)ServiceTimeout(20 seconds) --小概率類型 Service在特定的時間內無法處理完成

 

這三種原因都會造成ANR,但是第一種情況基本上占了所有ANR的百分之九十以上,第三種的情況微乎其微。這三種ANR不是孤立的,有可能會相互影響。例如一個應用程序進程中同時有一個正在顯示的Activity和一個正在處理消息的BroadcastReceiver,它們都運行在這個進程的主線程中。如果BR的onReceive函數沒有返回,此時用戶點擊屏幕,而onReceive超過5秒仍然沒有返回,主線程無法處理用戶輸入事件,就會引起第1種ANR。如果繼續超過10秒沒有返回,又會引起第2種ANR。發生ANR的主要原因是潛在的耗時操作,例如網絡或數據庫操作,或者高耗時的計算如改變位圖尺寸,應該在子線程裡(或者以數據庫操作為例,通過異步請求的方式)來完成。然而,不是說你的主線程阻塞在那裡等待子線程的完成——也不是調用 Thread.wait()或是Thread.sleep()。替代的方法是,主線程應該為子線程提供一個Handler,以便完成時能夠提交給主線程。以這種方式設計你的應用程序,將能保證你的主線程保持對輸入的響應性並能避免由於5秒輸入事件的超時引發的ANR對話框。

三種ANR發生時都會在log中輸出錯誤信息,你會發現各個應用進程和系統進程的函數堆棧信息都輸出到了一個/data/anr/traces.txt的文件中,ROOT手機導出該文件後,分析此日志可以得出一些結論,但traces文件信息比較抽象,難理解。總的來說,日志難收集,結果難分析。

 

1.2 如何避免ANR?

1) 運行在主線程裡的任何方法都盡可能少做事情。特別是,Activity應該在它的關鍵生命周期方法(如onCreate()和onResume())裡盡可能少的去做創建操作。(可以采用重新開啟子線程的方式,然後使用Handler+Message的方式做一些操作,比如更新主線程中的ui等)

2) 應用程序應該避免在BroadcastReceiver裡做耗時的操作或計算。但不再是在子線程裡做這些任務(因為 BroadcastReceiver的生命周期短),替代的是,如果響應Intent廣播需要執行一個耗時的動作的話,應用程序應該啟動一個 Service。(此處需要注意的是可以在廣播接受者中啟動Service,但是卻不可以在Service中啟動broadcasereciver,關於原因後續會有介紹,此處不是本文重點)

3)避免在Intent Receiver裡啟動一個Activity,因為它會創建一個新的畫面,並從當前用戶正在運行的程序上搶奪焦點。如果你的應用程序在響應Intent廣 播時需要向用戶展示什麼,你應該使用Notification Manager來實現。

TraceView是android SDK中自帶的一個性能測試工具,可以在Tools目錄下找到。TraceView能很精確的查看到每一個類,每一個類方法的執行時間。APP卡頓我們只需要保留當時的traceview文件,通過查看該文件,就可以定位絕大部分問題。

通過Debug.startMethodTracing(String FileName)和Debug.stopMethodTracing()來記錄一段時間內方法執行情況。

在主線程中不停的插入一個輕量級別的操作,如果該變量在指定的時間內,沒有改變,則說明此刻APP卡頓。卡頓工具工具就是根據https://github.com/SalomonBrys/ANR-WatchDog項目改進而成的。把ANR這種警告變成錯誤讓APP閃退,持久化當時信息。

原理圖:

\

問題背景:

 

百度國際化浏覽器初次安裝App,點擊icon後,明顯卡頓或者ANR,QA手工測試無法定位,RD優化代碼多次依舊找不到問題的節點。

測試方法:

百度國際化浏覽器加入卡頓工具jar,代碼中初始化。測試結果分析。

在打開traceview 文件後,通過 real Time/Call 從大到小排序,找到對應的與代碼相關消耗時間最大的方法。

\

我們能夠看到很明顯的看出FrameWindow.initDataBase()方法占用CPU過長達到3S左右了,距離5S很接近,通過查看代碼,結合業務邏輯,得知此處為數據庫初始化,並且主要是標簽數據庫初始化。

從整個APP來看,啟動頁面初始化標簽數據庫並沒有錯,但是此刻本身邏輯就非常多,標簽數據庫初始化後並沒有馬上使用到,而是到二級頁面才有查詢動作,總的來說,就是增加資源緊張。

建議:標簽數據庫什麼時候使用,什麼時候初始化。建議放到二級頁面初始化,減少頁面App頁面啟動的負荷,減少冷啟動時間,避免卡頓和ANR。並且標簽數據庫初始化放在線程中。

二、流暢度測試

流暢度測試簡單的來說就是Android頁面繪制。Android系統每秒60hz,也就是大約每16ms刷新一次界面。但是在我們使用APP過程中,經常會看到頁面有卡頓,或者說丟幀的現象。也就是說可能此刻兩個頁面繪制的時間差超過0.1S(人眼視覺殘留0.1S)。總的來說,就是頁面

原理分析

在確定衡量指標之前,我們先來研究一下Android的UI更新機制。

2.1 Android如何繪制UI?=

關於Android是如何更新UI,相信已經有很多文章介紹其中的步驟以及過程,大體上可以用下圖來展示:

\

從圖中可以看到無論那條路走下去始終都由SurfaceFlinger來控制最後的更新。

在Android版本更新過程中,發現在Jelly Bean中Google加入了一個Project Butter,用來解決嚴重影響Android口碑的問題之一“UI流暢性差”的問題。而Project Butter中主要引入了三個核心元素:VSYNC(垂直同步)、Triple Buffer和Choreographer。

2.2從VSYNC開始

VSync是Vertical Synchronization(垂直同步)的縮寫,是一種在PC上很早就廣泛使用的技術,可以簡單的把它認為是一種定時中斷。而在Android 4.1(JB)中已經開始引入VSync機制。

 

\

上圖所示是VSync機制下的繪制過程。從上圖可以看出,CPU和GPU的處理時間都少於一個VSync的間隔,即16.6ms。如果每個間隔都有繪制的情況下,當前的FPS即為60幀。

 

當CPU和GPU處理時間都很慢,或因為其他的原因,如在主線程中干活太多,那麼就會出現如下圖這樣的狀況。

\

從上圖可以看到,CPU和GPU的處理時間因為各種原因都大於一個VSync的間隔(16.6ms),所以在第二個VSync還在處理1區域的繪制時,不可能實現理論上的FPS60,同時也出現了丟幀(SF: Skipped Frame)情況。

 

為了便於理解,上圖用的是雙Buffer機制的情況,實際上Android 4.1引入了Triple Buffer,所以當雙Buffer不夠用時,Triple Buffer丟幀的情況如下圖所示。

\

VSync機制就像是播放動畫片(60幀/s)。每次都會播放畫面,有的時候有人偷懶了,機器壞了,就會出現播放速度降低的狀況。我們把這個播放速度叫做流暢度。

2.3 從FPS&丟幀到流暢度(SM: SMoothness)

實際上在很多Android的App中,很少有需要不斷地去繪制的場景,很多時候頁面都是靜態的。也就是會出現這樣的狀況,雖然1s中VSync的60個Loop不是每個都在做繪制的工作,FPS會比較低,但並不代表這個時候程序不流暢(如我將App放著不動,實測FPS為1)。所以FPS較低並不能代表當前App在UI上界面不流暢,而1s內VSync這個Loop運行了多少次更加能說明當前App的流暢程度。所以,下面這2個指標比FPS更能代表當前的App是否處於流暢的狀態。同樣這2個指標更加能夠量化App卡頓的程度:

1)丟幀(SF: Skipped Frame):如上圖2所示情況應該在16.6ms完成工作卻因各種原因沒做完,占了後n個16.6ms的時間,相當於丟了n幀。

2)流暢度(SM: SMoothness):和丟幀相對,在VSync機制中1s內Loop運行的次數。

和丟幀相對1s內有60個Loop因為某幾次工作時間超過了16.6ms(丟幀),這樣Loop就無法運行60次(理論最大值)。

當流暢度越小的時候說明當前程序越卡頓。

2.4數數:如何得到流暢度(SM: SMoothness)

接著上面的結論,如果在這樣的機制下每次Loop運行之前進行通知,記個數就好了。

很幸運我們在新的Android的那一套機制中找到了一個畫圖的打雜工Choreographer這個對象。根據Google的官方API文檔描述中,它是用來協調animations、input以及drawing時序的,並且每個Loop共用一個Choreographer對象。

下圖為Choreographer的定義和結構。

\

 

結論

通過如上原理分析可以得出結論:

1) Android 4.1引入了VSync機制後,可以通過其Loop來了解當前App最高繪制能力。

固定每隔16.6ms執行一次(這個值是一個靜態變量,會根據系統版本不同而采用不同的值,目前測試版本是16.6ms這樣最高的刷新的幀率就控制在60FPS以內);

如果沒有以上事件的時候同樣也會運行這樣一個Loop;

這個Loop在1s之內運行了多少次,即可以表示當前App繪制的最高的能力,也就是Android App卡頓的程度;

另外,在一次Loop時如果執行時間超過了16.6ms,那麼用多於16.6ms的時間除以16.6ms,即是當前App的丟幀(SF: Skipped Frame)。

2)可以在Choreographer的回調FrameCallback中,按秒計數表示當前App的流暢程度,即流暢度SM(SMoothness)。

采用這樣方式就可以在App內部觀測當前App的流暢度了。並且在丟幀的地方打印traceView,就可以知道丟幀的大概原因,大概位置。定位代碼問題。

三、 SmartMonkey測試

Android自動化測試中,monkey測試是一種傳統的穩定性測試工具。它可以隨機產生事件,不帶任何主觀性,並且使用方便。但是,正是由於這種隨機性,使得傳統的monkey測試只能作為穩定性測試工具,在其上進行功能擴展較為不易。在monkey測試中,由於事件的隨機性,使得monkey容易卡在某些簡單頁面,比如登陸頁面這種可操作內容很少的頁面。

3.1 針對這些問題,我們基於Robotium自動測試框架,開發了SmartMonkey工具。它具有以下這些特點:

准確識別頁面上的操作,避免無效點擊

支持關鍵路徑配置,使測試范圍可控

操作優先級動態變化,覆蓋更多功能和頁面

多進程基礎性能信息自動采集

結合性能專項工具,進一步挖掘性能隱患

支持Checklist配置,提供簡單的功能驗證

3.2如何使用

SmartMonkey工具本質上是一個大型的case,其使用方法也與普通的case執行方法相似。

建立一個Android Test Project工程

修改AndroidManifest.xml文件

修改instrumentation TAG中的name和targetPackage字段內容如下。

\

 

3.3導入SmartMonkey所需的lib

 

\

 

3.4在測試工程的src文件中,新建JUnit Test Case該類需繼承com.baidu.lynq.lynq.LynQBaseCase類。

 

\

 

3.5 在新建的Case中添加以下code

 

其中LAUNCHER_ACTIVITY_FULL_CLASSNAME為被測APP的launcher activity,TARGET_PACKAGE為被測包的包名。

privatestaticfinalStringLAUNCHER_ACTIVITY_FULL_CLASSNAME="com.example.android.apis.ApiDemos";

privatestaticfinalStringTARGET_PACKAGE="com.example.android.apis";

 

publicTest()throws ClassNotFoundException{

super(TARGET_PACKAGE, Class.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME));

}

/*

* Smart monkey示例

*/

publicvoidtest_sample()throws ClassNotFoundException {

mSolo.sleep(10000);

mSolo.analysis();

}

3.6添加配置項

 

在項目中新建assets文件夾,添加\AdvancedConfig.properties和config.properties文件

 

在config.properties文件中添加配置項:

# report path. MUST BE absolute path.

mReportPath =/mnt/sdcard/lynq-report

# login switch. value:{true, false}

# true -> automatic login when ENTER the LOGIN PAGE.

# login page MUST be baidupass. (http://wappass.baidu.com/passport/login)

mLoginSwitch =true

#username. using in login function

mUsername =USERNAME

# password. using in login function

mPassword =PASSWORD

 

其中mReportPath是設置輸出報告的位置, 默認為/mnt/sdcard/lynq-report/目錄,mLoginSwitch是自動登錄開關,當前僅限baidu passport SDK自動登錄。mUsername和mPassword分別為自動登錄時輸入的用戶名和密碼。

 

在AdvancedConfig.properties文件中添加配置項:

# time slot of performance collector. unit:ms. value:int

mTimeSlot =10000

# This is performance report type.value:int{0,1}defualtis 0,report ishtml file. when valueequels1,the report isxmlfile.

mPerformanceReportType =0

# sleep time. interval between two action. unit:ms. value:int

mSleepTime =1000

# default text foreditview. value: string

# NEVER enter any: commenting thisconfigitem OR make it empty

mDefaultText =defaulttextINsmartmonkey

# text for special EditText. split by "|", format: viewID1,content1|viewID2,content2

# viewID: EditText's id, content: text for this EditText

mSpInputText =1234,specialtextforeditTextwithid1234|123,specialtextforeditTextwithid123

# view never click. split by "|"

mNeverClick =\u9000\u51FA\u767B\u5F55|button_settings_logout

# view must click. split by "|"

mMustClick =positiveButton|\u53D6\u6D88

# max operation running time. working in mode1 && mode2.

# value: String

mMaxRunningTime =08:00:00

其中,mTimeSlot是性能數據采集的間隔,mPerformanceReportType是設置性能報告輸出樣式,1為xml樣式,0為html樣式。

 

mSleepTime是兩次事件的執行間隔,mDefaultText是默認的SmartMonkey在EditText中輸入的內容。mSpInputText中可以配置在某些輸入框中特殊輸入的內容用豎線分割,比如,”1234,specialtextforeditTextwithid1234”,表示在id為1234的view中,輸入“specialtextforeditTextwithid1234”內容。

 

mNeverClick和mMustClick用來表示關鍵路徑,分別為避免點擊的view和必點的view。View可以用文字或十進制id或id string來表示。例如下圖中右下角OK按鈕,在R文件中是“publicstaticfinalintbtn_ok=0x7f09001d; ”,所以,這個view可以用其上文字“OK”來表示,也可以用id string “btn_ok”或十進制id“2131296285”來表示。

 

mMaxRunningTime是SmartMonkey的最大執行時間,用時分秒來表示。

 

\

 

3.7執行

通過Run as Android Junit Test方式執行。

四、查看輸出報告

4.1 crash信息

SmartMonkey會自動記錄被測APP的crash棧信息,以及native crash信息。

Crash信息會輸出在你配置的目錄中,以stack為開頭的txt文件。每個crash單獨輸出一個文件。Native crash信息記錄在以dmp開頭的文件中,可以通過google-breakpad進行查看。

4.2基礎性能報告

根據配置項,SmartMonkey會輸出性能報告到輸出報告目錄中。性能報告是以performance開頭的html或xml文件。

Html格式的性能報告中,首先會列出被測app的相關信息,包括包名、uid和同uid下的每一個進程的pid和進程名等。隨後列出CPU、內存、流量的圖表。CPU圖表中記錄了每一個進程的CPU占用率,內存圖表中記錄了每個進程PSS和USS的占用情況,流量圖表中記錄了流量總使用情況和兩個采集點之間的流量差值。

在每個圖表上,用node記錄了這個節點上SmartMonkey執行的事件,可以用來輔助定位造成曲線波動的操作。

\

\

Xml格式的性能報告中,每個operation為一個采集點,其中記錄了時間戳、測試手機總CPU占用率、流量差值、流量總和、節點上的事件,以及每個進程的pid、CPU占用率、PSS、USS等。

\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved