Android性能相關--性能指標篇

性能指標

在這裏插入圖片描述

流暢性

FPS:SurfaceFlinger 合成次數
  1. FPS低可能是當前沒有內容更新!
  2. 屏幕內若有多塊顯示區域(Surface),則我們的區域可能受影響
  3. 某些Surface合成不在SF中進行(Camera),則FPS無法衡量

FPS的計算,系統提供adb命令service call SurfaceFlinger 1013來獲取從啓動當前SF一共進行了多少次合成。
T1時間獲取合成次數V1,T2時間獲取合成次數V2
FPS = (V2-V1)/(T2-T1)

掉幀率
  1. SF:修改系統屬性debug.choreographer.skipwarning=1,則丟幀時都會打印日誌。(限制:adb需要root權限)
    setprop debug.choreographer.skipwarning 1
    setprop ctl.restart surfaceflinger; setprop ctl.restart zygote

  2. 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;
        }
    }
  1. 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端,可以進行整合、擴展,這是下一部分的內容了。
各主要數據獲取途徑上面已經描述,除了上述的重要數據還有其他很多比如

  1. Thread.getStackTrace() 獲取各線程的棧信息採集
  2. Hook android.database.sqlite.SQLiteDatabase的關鍵函數來監控SQLite數據庫的操作
  3. 通過cmd命令記錄log信息
    等等各方面的採集。

我們通過GT能獲得的性能數據如下:

CPU指標、內存指標、流量指標
在這裏插入圖片描述

流暢度 – 幀率丟失、Activity/Fragment的冷熱啓動時長
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

流暢度 – 壓力測試
Monkey 和 Monkey Demons
電量指標
GT有【耗電數據採集】插件,但只支持個別機型。

擴展

我們將GT SDK集成進來,並對其進行擴展,提供更多數據的收集功能,比如內存泄漏相關、自定義函數數據觀測等。再將GT APP的數據整理 展示部分集成過來,最終將這些結果用同樣的方式展示在網頁上,形成一個完整的性能收集方案

擴展內容:(目前只有這兩個)

  1. 藉助LeakCanary等工具收集內存泄漏信息
  2. 提供註解@observeMethod,可以觀測函數運行時間、CPU變化、內存變化、流量變化等

數據整理 展示部分整合
在這裏插入圖片描述
PS:左邊App進程部分處於sdk中的內容,右邊數據接收數據並整理部分處於GT APP中,將兩部分進行整合優化,形成一套SDK。
最終將data.js放到GT_Report中查看數據

PS:
android平臺局域網服務器搭建
https://github.com/koush/AndroidAsync
https://github.com/koush/AndroidAsync

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