Android性能指标大致分为以下几点
FPS、CPU使用率、内存占用、页面加载时间、网络请求
内存使用
在加载图片、视频、声音等这些比较耗费内存的资源的时候去获取一次应用内存占用及系统剩余内存保存到日志中。
Android提供了API可以获取系统总内存大小及当前可用内存大小,以及获取应用中内存的使用情况。
Runtime runtime = Runtime.getRuntime();
long totalSize = runtime.maxMemory() >> 10;
this.memoryUsage = (runtime.totalMemory() - runtime.freeMemory()) >> 10;
this.memoryUsageRate = this.memoryUsage * 100 / totalSize;
CPU使用
可以每隔一段时间去获取APP进程CPU时间,也可以针对一些场景手动获取。可以设定一个阈值,比如大于50%的时候,将各个线程名及线程占用的CPU时间一起保存到日志中。
系统没有提供API接口,可以直接在应用中读取/proc/stat,/proc/pid/stat,/proc/pid/task/tid/stat这几个文件来获取,不需要root权限
int mPid;
long mLastCpuTime;//当前手机的CPU总时间
long mLastAppTime;//当前app的CPU耗时
RandomAccessFile procStatFile;
static final int RATE_LIMIT = 50;//CPU占比阈值
if (mPid == 0) {
mPid = android.os.Process.myPid();
}
try {
if (procStatFile == null || appStatFile == null) {
procStatFile = new RandomAccessFile("/proc/stat", "r");
appStatFile = new RandomAccessFile("/proc/" + mPid + "/stat", "r");
}
//文件开头
procStatFile.seek(0);
appStatFile.seek(0);
String proStatSrc = procStatFile.readLine();
String appStatSrc = appStatFile.readLine();
String[] proStats = null;
String[] appStats = null;
if (proStatSrc != null || appStatSrc != null) {
proStats = proStatSrc.trim().split(" ");
appStats = appStatSrc.trim().split(" ");
}
long cpuTime = 0L;
long appTime = 0L;
if (proStats != null && proStats.length >= 9) {
for (int i = 2; i <= 8; i++) {
cpuTime += Long.valueOf(proStats[i]);
}
}
if (appStats != null && appStats.length >= 15) {
appTime = Long.valueOf(appStats[13]) + Long.valueOf(appStats[14]);
}
if (mLastCpuTime == 0 || mLastAppTime == 0) {
mLastCpuTime = cpuTime;
mLastAppTime = appTime;
return;
}
double cpuRate = (double) (appTime - mLastAppTime) / (double) (cpuTime - mLastCpuTime);
mLastCpuTime = cpuTime;
mLastAppTime = appTime;
//CPU使用率
int cpuRateInt = (int) (cpuRate * 100);
//TODO
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.d(TAG, "init randomfile failed");
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "read file failed");
}
}
FPS(卡顿)
Android系统从4.1(API 16)开始加入Choreographer这个类来控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个操作。其实UI显示的时候每一帧要完成的事情只有这三种。Choreographer接收显示系统的时间脉冲(垂直同步信号-VSync信号),在下一个帧渲染时控制执行这些操作。收到VSync信号后,顺序执行3个操作,然后等待下一个信号,再次顺序执行3个操作。假设在第二个信号到来之前,所有的操作都执行完成了,即Draw操作完成了,那么第二个信号来到时,此时界面将会更新为第一帧的内容,因为Draw操作已经完成了。否则界面将不会更新,还是现实上一个帧的内容,表示丢帧了。丢帧是造成卡顿的原因。
通过 Choreographer 类设置它的 FrameCallback,可以在每一帧被渲染的时候记录下它开始渲染的时间,每一次同步的周期为16ms,代表一帧的刷新频率,一次界面渲染会回调 FrameCallback的doFrame(longframeTimeNanos)方法,如果两次 doFrame 之间的间隔大于16ms说明丢帧了。用这种方法,可以实时监控应用的丢帧情况。
static final int FPS_LIMIT = 30;//最低帧率,低于这个帧代表页面不流畅
static final int INTERVAL = 300;//Frame采样时间
@Override
public void doFrame(long frameTimeNanos) {
long currentTimeMillis = TimeUnit.NANOSECONDS.toMillis(frameTimeNanos);
if (mLastFrameStartTime > 0) {
//两帧的时间间隔
final long timeSpace = currentTimeMillis - mLastFrameStartTime;
mFrameRendered++;
if (timeSpace >= INTERVAL) {
//计算帧率
mFPS = (int) (mFrameRendered * 1000 / timeSpace);
mLastFrameStartTime = currentTimeMillis;
mFrameRendered = 0;
}
} else {
mLastFrameStartTime = currentTimeMillis;
}
mChoreographer.postFrameCallback(this);
}
使用:
if (mFPS < FPS_LIMIT) {
...
} else {
...
}
页面加载时间
如何判定UI渲染完成?
在Activity的rootView中插入一个FrameLayout,并且监听这个FrameLayout是否调用了dispatchDraw方法实现的
class AutoSpeedFrameLayout extends FrameLayout {
public static View wrap(int pageObjectKey, @NonNull View child) {
...
//将页面根View作为子View,其他参数保持不变
ViewGroup vg = new AutoSpeedFrameLayout(child.getContext(), pageObjectKey);
if (child.getLayoutParams() != null) {
vg.setLayoutParams(child.getLayoutParams());
}
vg.addView(child, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return vg;
}
private final int pageObjectKey;//关联的页面key
private AutoSpeedFrameLayout(@NonNull Context context, int pageObjectKey) {
super(context);
this.pageObjectKey = pageObjectKey;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
AutoSpeed.getInstance().onPageDrawEnd(pageObjectKey);
}
}
自定义了一层 FrameLayout 作为所有页面根View的父View,其 dispatchDraw() 方法执行super后,记录相关页面绘制结束的时间点。
网络请求
一,流量
具体方案:后台定时任务->获取间隔内流量->记录前后台->分别计算->上报 后台->流量治理依据
实现方法:
1.TrafficStats
2.可使用okhttp的interceptor中监控每个api的流量,但无法获知其他如webview流量
3.stetho
二,成功率
返回状态码
三,速度
计算每次request和response之间时间间隔