性能指標
流暢性
FPS:SurfaceFlinger 合成次數
- FPS低可能是當前沒有內容更新!
- 屏幕內若有多塊顯示區域(Surface),則我們的區域可能受影響
- 某些Surface合成不在SF中進行(Camera),則FPS無法衡量
FPS的計算,系統提供adb命令service call SurfaceFlinger 1013來獲取從啓動當前SF一共進行了多少次合成。
T1時間獲取合成次數V1,T2時間獲取合成次數V2
FPS = (V2-V1)/(T2-T1)
掉幀率
-
SF:修改系統屬性debug.choreographer.skipwarning=1,則丟幀時都會打印日誌。(限制:adb需要root權限)
setprop debug.choreographer.skipwarning 1
setprop ctl.restart surfaceflinger; setprop ctl.restart zygote -
SM:代碼注入,監控Choreographer.FrameCallback,每調用一次就是一次渲染,相鄰兩次之間時間超過16ms則丟幀(限制:需要嵌入代碼)
long lastTime = 0;
long thisTime = 0;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
void startMonitor(){
Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {//系統繪幀回調
public void doFrame(long frameTimeNanos) {
thisTime = System.currentTimeMillis();
//累計流暢值
plusSM(thisTime);//當前秒的SM+1,如果當前秒數已經到下一秒,則將此SM值寫入文件
//判別超時:
if (thisTime - lastTime > 40 && lastTime!=0){
Log.e("DDDD","frame超時"+(thisTime - lastTime)+"ms: "+ lastTime +"-"+ thisTime);
//saveBlockInfo(lastTime, thisTime);//此處保存卡頓信息
}
//設置下一幀繪製的回調
Choreographer.getInstance().postFrameCallback(this);//設置下次系統繪幀回調
lastTime = thisTime;
}
};
Choreographer.getInstance().postFrameCallback(frameCallback);
}
//保存當前SM值
private long nowTime =1;//當前的時間(ms)
private int sm = 1;
private void plusSM(long t){
if (nowTime ==1){
nowTime = t;
}
if (nowTime/1000 == t/1000){
sm++;
}else if (t/1000 - nowTime/1000 >=1){
//saveSMInfo(sm,t);//此處保存此時的流暢值SM
Log.e("DDDD","sm:"+sm);
sm=1;
nowTime = t;
}
}
- Janky frames:
adb shell dumpsys gfxinfo < PACKAGE_NAME > 打印最近128幀信息
Graphics info for pid 31148 [com.android.settings]: 表明當前dump的爲設置界面的幀信息,pid爲31148
Total frames rendered: 105 本次dump蒐集了105幀的信息
Janky frames: 2 (1.90%) 105幀中有2幀的耗時超過了16ms,卡頓概率爲1.9%
Number Missed Vsync: 0 垂直同步失敗的幀
Number High input latency: 0 處理input時間超時的幀數
Number Slow UI thread: 2 因UI線程上的工作導致超時的幀數
Number Slow bitmap uploads: 0 因bitmap的加載耗時的幀數
Number Slow issue draw commands: 1 因繪製導致耗時的幀數
Activity啓動時長、首頁啓動時長
Hook點
hook android.app.Instrumentation.execStartActivity函數 :startActivity函數
hook android.app.Instrumentation.callActivityOnCreate函數 :onCreate函數
hook android.app.Instrumentation.callActivityOnStart函數 :onStart函數
hook android.app.Instrumentation.callActivityOnResume函數 :onResume函數
hook android.app.Instrumentation.callActivityOnPause函數 :onPause函數
hook android.app.Instrumentation.callActivityOnStop函數 :onStop函數
Fragment啓動時長
Hook點
//android.app.Fragment包:
hook android.app.Fragment.onAttach :onAttach
hook android.app.Fragment.performCreate :onCreate
hook android.app.Fragment.performCreateView :onCreateView
hook android.app.Fragment.performActivityCreated :onActivityCreated
hook android.app.Fragment.performStart :onStart
hook android.app.Fragment.performResume :onResume
hook android.app.Fragment.performPause :onPause
hook android.app.Fragment.performStop :onStop
hook android.app.Fragment.performDestroyView :onDestoryView
hook android.app.Fragment.performDestroy :onDestory
hook android.app.Fragment.performDetach :onDetach
hook android.app.Fragment.onHiddenChanged :onHiddenChanged
hook android.app.Fragment.setUserVisibleHint :setUserVisibleHint
//android.support.v4.app.Fragment包:
hook android.support.v4.app.Fragment.onAttach :onAttach
hook android.support.v4.app.Fragment.performCreate :onCreate
hook android.support.v4.app.Fragment.performCreateView :onCreateView
hook android.support.v4.app.Fragment.performActivityCreated :onActivityCreated
hook android.support.v4.app.Fragment.performStart :onStart
hook android.support.v4.app.Fragment.performResume :onResume
hook android.support.v4.app.Fragment.performPause :onPause
hook android.support.v4.app.Fragment.performStop :onStop
hook android.support.v4.app.Fragment.performDestroyView :onDestoryView
hook android.support.v4.app.Fragment.performDestroy :onDestory
hook android.support.v4.app.Fragment.performDetach :onDetach
hook android.support.v4.app.Fragment.onHiddenChanged :onHiddenChanged
hook android.support.v4.app.Fragment.setUserVisibleHint :setUserVisibleHint
CPU數據
/proc文件系統是一個僞文件系統,它只存在內存當中,而不佔用外存空間。它以文件系統的方式爲內核與進程提供通信的接口。用戶和應用程序可以通過/proc得到系統的信息,並可以改變內核的某些參數。
/proc/cpuinfo 獲取CPU硬件信息(型號、頻率、緩存大小等)
/proc/stat 獲取CPU總體活動信息
/proc/< pid >/stat 獲取某進程的CPU活動信息
/proc/< pid >/task/< tid >/stat 獲取某線程的CPU活動信息
eg:stat文件獲取到的信息的單位是jiffies(時間片,記錄系統啓動以來的節拍數,不同內核一個節拍的時間不同,通常1~10ms)
/proc/stat
fjzag@fjzag-desktop:~$ cat /proc/stat
cpu 38082 627 27594 893908 12256 581 895 0 0
cpu0 22880 472 16855 430287 10617 576 661 0 0
cpu1 15202 154 10739 463620 1639 4 234 0 0
intr 120053 222 2686 0 1 1 0 5 0 3 0 0 0 47302 0 0 34194 29775 0 5019 845 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 1434984
btime 1252028243
processes 8113
procs_running 1
procs_blocked 0
- user (38082) 從系統啓動開始累計到當前時刻,處於用戶態的運行時間,不包含 nice值爲負進程。
- nice (627) 從系統啓動開始累計到當前時刻,nice值爲負的進程所佔用的CPU時間
- system (27594) 從系統啓動開始累計到當前時刻,處於核心態的運行時間
- idle (893908) 從系統啓動開始累計到當前時刻,除IO等待時間以外的其它等待時間iowait (12256) 從系統啓動開始累計到當前時刻,IO等待時間(since 2.5.41)
- irq (581) 從系統啓動開始累計到當前時刻,硬中斷時間(since 2.6.0-test4)
- softirq (895) 從系統啓動開始累計到當前時刻,軟中斷時間
- guest_nice 從系統啓動開始累計到當前時刻,在Linux內核控制下的操作系統虛擬cpu花費在nice進程上的時間
/proc/< pid >/stat 和 /proc/< pid >/task/< tid >/stat
[zhengangen@buick ~]# cat /proc/6873/stat
1 (linuxrc) S 0 0 0 0 -1 8388864 50 633 20 4 2 357 72 342 16 0 1 0 22 2252800 70 4294967295 32768 1879936 3199270704 3199269552 1113432 0 0 0 674311 3221479524 0 0 0 0 0 0
看下 2 357 72 342 這段數據
- utime=2 該任務在用戶態運行的時間
- stime=357 該任務在覈心態運行的時間
- cutime=72 累計的該任務的所有的waited-for進程曾經在用戶態運行的時間
- cstime=342 累計的該任務的所有的waited-for進程曾經在覈心態運行的時間
計算方法:
- 某時間段內系統CPU使用率:
1.將T2和T1的總CPU時間片(user、nice、system、idle、iowait、irq、softirq、stealstolen、guest相加)相減,得到這段時間的總CPU時間片s1
2.將T2.idle和T1.idle相減得到這段時間的總CPU空閒時間s2
3.該時間系統CPU使用率爲 100 * ( s1 - s2 )/ s1 - 某時間段內某進程CPU使用率:
1.將T2和T1的總CPU時間片(user、nice、system、idle、iowait、irq、softirq、stealstolen、guest相加)相減,得到這段時間的總CPU時間片s1
2.將T2和T1的該進程總CPU時間片(utime、stime、cutime、cstime相加)相減,得到這段時間內的該進程CPU時間片s2
3.該時間段內該進程CPU使用率爲:100 * s2 / s1
內存
系統總內存的獲取
讀取/proc/meminfo文件的第一個字段MemTotal即可
系統空閒內存
//通過ActivityManager獲取
public static long getSysFreeMemory(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
am.getMemoryInfo(mi);
return mi.availMem;
}
進程內存
//進程內存上限
//進程內存上限
public static int getMemoryMax() {
return (int) (Runtime.getRuntime().maxMemory()/1024);
}
//進程總內存
public static int getPidMemorySize(int pid, Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int[] myMempid = new int[] { pid };
Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
int memSize = memoryInfo[0].getTotalPss();
return memSize;
}
系統內存快照
adb shell dumpsys meminfo < packageName >
流量
TrafficStats類是由Android提供的一個從你的手機開機開始,累計到現在使用的流量總量,或者統計某個或多個進程或應用所使用的流量,當然這個流量包括的Wifi和移動數據網Gprs。
//系統流量統計:
TrafficStats.getTotalRxBytes() ——獲取從此次開機起總接受流量(流量是分爲上傳與下載兩類的,當然其實這裏還有本地文件之間數據交換的流量,這個暫且不說,等下說明一下我遇到的問題);
TrafficStats.getTotalTxBytes()——獲取從此次開機起總髮送流量;
TrafficStats.getMobileRxBytes()——獲取從此次開機起不包括Wifi的接受流量,即只統計數據網Gprs接受的流量;
TrafficStats.getMobileTxBytes()——獲取從此次開機起不包括Wifi的發送流量,即只統計數據網Gprs發送的流量;
//進程流量統計:
TrafficStats.getUidRxBytes(mUid)//上行流量
TrafficStats.getUidTxBytes(mUid)//下行流量
佈局繪製
繪製時長
View在使用之前需要進行Infalte操作,此操作在主線程執行且耗時嚴重,通常是造成卡頓的直接原因
//hook的函數:
hook android.view.LayoutInflater.inflate :inflate
hook android.app.Activity.setContentView :setContentView
從setContentView到inflate結束,這段時間用時可看作繪製時長,一般超過30ms則認爲構建超時。
繪製深度
https://github.com/Tencent/GT/blob/master/android/GT-佈局檢測原理及規則.md
GT神器威力加強版
GT提供的能力
GT這部分可以通過下載其APK來使用,其提供的sdk只提供了數據收集能力,數據處理在GT APP端,可以進行整合、擴展,這是下一部分的內容了。
各主要數據獲取途徑上面已經描述,除了上述的重要數據還有其他很多比如
- Thread.getStackTrace() 獲取各線程的棧信息採集
- Hook android.database.sqlite.SQLiteDatabase的關鍵函數來監控SQLite數據庫的操作
- 通過cmd命令記錄log信息
等等各方面的採集。
我們通過GT能獲得的性能數據如下:
CPU指標、內存指標、流量指標
流暢度 – 幀率丟失、Activity/Fragment的冷熱啓動時長
流暢度 – 壓力測試
Monkey 和 Monkey Demons
電量指標
GT有【耗電數據採集】插件,但只支持個別機型。
擴展
我們將GT SDK集成進來,並對其進行擴展,提供更多數據的收集功能,比如內存泄漏相關、自定義函數數據觀測等。再將GT APP的數據整理 展示部分集成過來,最終將這些結果用同樣的方式展示在網頁上,形成一個完整的性能收集方案
擴展內容:(目前只有這兩個)
- 藉助LeakCanary等工具收集內存泄漏信息
- 提供註解@observeMethod,可以觀測函數運行時間、CPU變化、內存變化、流量變化等
數據整理 展示部分整合
PS:左邊App進程部分處於sdk中的內容,右邊數據接收數據並整理部分處於GT APP中,將兩部分進行整合優化,形成一套SDK。
最終將data.js放到GT_Report中查看數據
PS:
android平臺局域網服務器搭建
https://github.com/koush/AndroidAsync
https://github.com/koush/AndroidAsync