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;
}
}
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 因绘制导致耗时的帧数
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函数
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
/proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为内核与进程提供通信的接口。用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的某些参数。
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
/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 这段数据
计算方法:
读取/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这部分可以通过下载其APK来使用,其提供的sdk只提供了数据收集能力,数据处理在GT APP端,可以进行整合、扩展,这是下一部分的内容了。
各主要数据获取途径上面已经描述,除了上述的重要数据还有其他很多比如
我们通过GT能获得的性能数据如下:
流畅度 – 帧率丢失、Activity/Fragment的冷热启动时长
流畅度 – 压力测试
Monkey 和 Monkey Demons
电量指标
GT有【耗电数据采集】插件,但只支持个别机型。
我们将GT SDK集成进来,并对其进行扩展,提供更多数据的收集功能,比如内存泄漏相关、自定义函数数据观测等。再将GT APP的数据整理 展示部分集成过来,最终将这些结果用同样的方式展示在网页上,形成一个完整的性能收集方案
扩展内容:(目前只有这两个)
数据整理 展示部分整合
PS:左边App进程部分处于sdk中的内容,右边数据接收数据并整理部分处于GT APP中,将两部分进行整合优化,形成一套SDK。
最终将data.js放到GT_Report中查看数据
PS:
android平台局域网服务器搭建
https://github.com/koush/AndroidAsync
https://github.com/koush/AndroidAsync
微信APM开源项目Matrix
https://github.com/tencent/matrix#matrix_cn