这里只说明我自己获取FPS、丢帧率等信息来测试流畅度的自动化代码,具体的业务技术知识和手工测试的方法可参考另两篇博客:
话不多说,进入正题~
当渲染时间大于16.67,按照垂直同步机制,该帧就已经渲染超时,那么,如果它正好是16.67的整数倍,比如66.68,则它花费了4个垂直同步脉冲,减去本身需要一个,则超时3个;如果它不是16.67的整数倍,比如67,那么它花费的垂直同步脉冲应向上取整,即5个,减去本身需要一个,即超时4个,可直接算向下取整
最后的计算方法思路:
执行一次命令,总共收集到了m帧(理想情况下m=128),但是这m帧里面有些帧渲染超过了16.67毫秒,算一次jank,一旦jank,
需要用掉额外的垂直同步脉冲。其他的就算没有超过16.67,也按一个脉冲时间来算(理想情况下,一个脉冲就可以渲染完一帧)
所以FPS的算法可以变为:
m / (m + 额外的垂直同步脉冲) * 60
package getResource;
import commons.Commons;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
public class GetFps {
//清空之前采样的数据,防止统计重复的时间
private static String clearCommand = "adb shell dumpsys SurfaceFlinger --latency-clear";
private static long jumpingFrames = 0; //jank次数,跳帧数
private static long totalFrames = 0; //统计的总帧数
private static float lostFrameRate = 0; //丢帧率
private static float fps; //fps值
public static float[] getInfo(String deviceName, String packageName){
String gfxCMD = "adb -s " + deviceName + " shell dumpsys gfxinfo " + packageName;
float[] info = new float[2];
int vsyncOverTimes = 0; // 垂直同步次数
try {
Runtime.getRuntime().exec(clearCommand);
Process process = Runtime.getRuntime().exec(gfxCMD);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line; boolean flag = false;
while ((line = reader.readLine()) != null){
if (line.length() > 0){
if (line.contains("Execute")){
flag = true;
continue;
}
if (line.contains("View hierarchy")){
break;
}
if (flag){
String[] times = line.trim().split("\\s+");
//计算一帧所花费的时间
float onceTime = Float.parseFloat(times[0]) + Float.parseFloat(times[1]) + Float.parseFloat(times[2]) + Float.parseFloat(times[3]);
totalFrames += 1; //统计总帧数
if (onceTime > 16.67){//以Android定义的60FPS为标准
jumpingFrames += 1; // 统计跳帧jank数
//统计额外花费垂直同步脉冲的数量
if (onceTime % 16.67 == 0){
vsyncOverTimes += onceTime / 16.67 - 1;
}else {
vsyncOverTimes += Math.floor(onceTime / 16.67); //向下取整即可
}
}
}
}
}
if (totalFrames > 0){
fps = totalFrames / (totalFrames + vsyncOverTimes) * 60;
lostFrameRate = jumpingFrames / totalFrames;
info[0] = fps;
info[1] = (float) Commons.streamDouble(lostFrameRate * 100);
}else {
System.err.println("【ERROR】无FPS信息,请确认手机正常连接或APP正常运行");
}
} catch (IOException e) {
e.printStackTrace();
}
return info;
}
public static double getFps(String deviceName,String packageName){
return getInfo(deviceName,packageName)[0];
}
public static double getLostFrameRate(String deviceName,String packageName){
return getInfo(deviceName,packageName)[1];
}
}
另外再提供一个参考文档:移动性能测试 FPS 计算方法的比较