神刀安全网

GT流畅度测试-Choreographer

GT源码: https://github.com/TencentOpen/GT

在GT工具的插件中有一个可以测试应用流畅度的插件,通过其源码,去了解下GT是如何去测试流畅度的。

GT流畅度测试-Choreographer

SMActivity

先看界面上几个按钮监听事件的代码:

  • 检测
View.OnClickListener button_check_status = new View.OnClickListener() {

@Override
public void onClick(View v) {
String cmd = "getprop debug.choreographer.skipwarning";
ProcessBuilder execBuilder = new ProcessBuilder("sh", "-c", cmd);
execBuilder.redirectErrorStream(true);
try {
TextView textview = (TextView) findViewById(R.id.textviewInformation);
Process p = execBuilder.start();
InputStream is = p.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
Boolean flag = false;
String line;
while ((line = br.readLine()) != null) {
if (line.compareTo("1") == 0) {
flag = true;
break;
}
}

if (flag) {
textview.setText("OK");
} else {
textview.setText("NOT OK");
}
} catch (IOException e) {
e.printStackTrace();
}
}
};

  • 更改
View.OnClickListener button_write_property = new View.OnClickListener() {

@Override
public void onClick(View v) {
String cmd = "setprop debug.choreographer.skipwarning 1";
ProcessBuilder execBuilder = new ProcessBuilder("su", "-c", cmd);
execBuilder.redirectErrorStream(true);
try {
execBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
};

  • 恢复
View.OnClickListener button_recover_property = new View.OnClickListener() {

@Override
public void onClick(View v) {
String cmd = "setprop debug.choreographer.skipwarning 30";
ProcessBuilder execBuilder = new ProcessBuilder("su", "-c", cmd);
execBuilder.redirectErrorStream(true);
try {
execBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
};

  • 重启
View.OnClickListener button_restart = new View.OnClickListener() {

@Override
public void onClick(View v) {
String cmd = "setprop ctl.restart surfaceflinger; setprop ctl.restart zygote";
ProcessBuilder execBuilder = new ProcessBuilder("su", "-c", cmd);
execBuilder.redirectErrorStream(true);
try {
execBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
};

基本流程就是:检测 >> 更改 >> 重启 >> 恢复

检测的时候,执行了命令 getprop debug.choreographer.skipwarning

只有在执行该命令后,输出内容为 1 的时候,才会显示 OK ,即表示此时可以进行流畅度的测试

这个值在默认情况下,获取到的是空值。为什么要设为 1,才表示环境OK呢?

先记住 TAG 的值:

private static final String TAG = "Choreographer";

从下面的代码中可以看到,虽然在默认情况下获取不到 skipwarning 的值,但其实系统以及默认设为了 30,这也是为什么在 恢复 按钮的监听代码中将其值设为 30 的原因。

// Set a limit to warn about skipped frames.
// Skipped frames imply jank.
private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt(
"debug.choreographer.skipwarning", 30);

接下来看下面的代码:

long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}

其中

mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

由于 SKIPPED_FRAME_WARNING_LIMIT 的值默认为 30,因此在一般情况下,通过命令 adb logcat - v time -s Choreographer ,很少看到有日志信息的输出,因此需要将 SKIPPED_FRAME_WARNING_LIMIT 的值设为 1,这样可以保证只要有丢帧,jitterNanos >= mFrameIntervalNanos 的值就为 true,这样就会有日志信息输出。

在设置了 debug.choreographer.skipwarning 之后,需要重启 Zygote ,因此重启按钮的监听代码中使用了命令 setprop ctl.restart surfaceflinger; setprop ctl.restart zygote 进行重启。

另外通过代码

ProcessBuilder execBuilder = new ProcessBuilder("su", "-c", cmd);

执行这些命令都需要拥有Root权限!

都设置完成后就可以在logcat中看到有丢帧的日志信息输出:

[xuxu:~]$ adb logcat -c && adb logcat -v time -s Choreographer
--------- beginning of main
--------- beginning of system
04-30 17:45:52.927 I/Choreographer(25260): Skipped 3 frames! The application may be doing too much work on its main thread.
04-30 17:45:53.742 I/Choreographer(25260): Skipped 12 frames! The application may be doing too much work on its main thread.
04-30 17:45:53.866 I/Choreographer(25260): Skipped 6 frames! The application may be doing too much work on its main thread.
04-30 17:45:53.931 I/Choreographer(25260): Skipped 2 frames! The application may be doing too much work on its main thread.
04-30 17:45:54.108 I/Choreographer(25260): Skipped 10 frames! The application may be doing too much work on its main thread.
04-30 17:45:54.488 I/Choreographer(25260): Skipped 21 frames! The application may be doing too much work on its main thread.
04-30 17:45:54.576 I/Choreographer(25260): Skipped 4 frames! The application may be doing too much work on its main thread.
04-30 17:45:54.644 I/Choreographer(25260): Skipped 3 frames! The application may be doing too much work on its main thread.
04-30 17:45:54.910 I/Choreographer(25260): Skipped 1 frames! The application may be doing too much work on its main thread.
04-30 17:45:58.390 I/Choreographer(25260): Skipped 1 frames! The application may be doing too much work on its main thread.
04-30 17:45:59.501 I/Choreographer(25260): Skipped 2 frames! The application may be doing too much work on its main thread.
04-30 17:45:59.634 I/Choreographer(25260): Skipped 7 frames! The application may be doing too much work on its main thread.

SMLogService

protected void onHandleIntent(Intent intent) {
try {

String str = intent.getStringExtra("pid");
int pid = Integer.parseInt(str);

List<String> args = new ArrayList<String>(Arrays.asList("logcat", "-v", "time", "Choreographer:I", "*:S"));

dumpLogcatProcess = RuntimeHelper.exec(args);
reader = new BufferedReader(new InputStreamReader(dumpLogcatProcess.getInputStream()), 8192);

String line;

while ((line = reader.readLine()) != null && !killed) {

// filter "The application may be doing too much work on its main thread."
if (!line.contains("uch work on its main t")) {
continue;
}
int pID = LogLine.newLogLine(line, false).getProcessId();
if (pID != pid){
continue;
}

line = line.substring(50, line.length() - 71);
Integer value = Integer.parseInt(line.trim());

SMServiceHelper.getInstance().dataQueue.offer(value);
}
} catch (IOException e) {
Log.e(TAG, e.toString() + "unexpected exception");
} finally {
killProcess();
}
}

执行logcat命令获取log信息:

List<String> args = new ArrayList<String>(Arrays.asList("logcat", "-v", "time", "Choreographer:I", "*:S"));

log信息内容为:

04-30 17:45:52.927 I/Choreographer(25260): Skipped 3 frames!  The application may be doing too much work on its main thread.

无聊的同学可以数下,截取的结果是什么。。

line = line.substring(50, line.length() - 71);
Integer value = Integer.parseInt(line.trim());

SMDataService

while (true) {
if (pause) {
break;
}
int x = count.getAndSet(0);
// 卡顿大于60时,要将之前几次SM计数做修正
if (x > 60) {
int n = x / 60;
int v = x % 60;
TagTimeEntry tte = OpPerfBridge.getProfilerData(key);
int len = tte.getRecordSize();
// 补偿参数
int p = n;
//Math.min(len, n);
/*
* n > len是刚启动测试的情况,日志中的亡灵作祟,这种情况不做补偿;
* 并且本次也记为60。本逻辑在两次测试间会清理数据的情况生效。
*/

if (n > len) {
globalClient.setOutPara(key, 60);
// globalClient.setOutPara(SFKey, 0);
} else {
for (int i = 0; i < p; i++) {
TimeEntry te = tte.getRecord(len - 1 - i);
te.reduce = 0;
}
globalClient.setOutPara(key, v);
// globalClient.setOutPara(SFKey, 60 - v);
}
} else {
int sm = 60 - x;
globalClient.setOutPara(key, sm);
// globalClient.setOutPara(SFKey, x);
}

这里面是对获取到的数据做一些修正。

end…

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » GT流畅度测试-Choreographer

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址