[037]Choreographer Skipped含义再探

2020-06-08 12:06:40 浏览数 (1)

前言

在[036]Choreographer Skipped真正含义中,我介绍了一种可以产生Choreographer Skipped的情况。就是在onVsync被调用之前,往主线程post的一个Message。那还有没有其他方式可以产生这个Choreographer Skipped呢?

一、仔细看看代码

代码语言:javascript复制
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
    ...
    //onVsync方法将会在Vsync信号接收之后被回调
    //mTimestampNanos就是这次Vsync信号接收的时间
    mTimestampNanos = timestampNanos;
    mFrame = frame;
    //往主线程的Looper中投放一个Asynchronous的Message,callback为this
    //这个Message被处理的时候就会调用下面run-doFrame的方法
    Message msg = Message.obtain(mHandler, this);
    msg.setAsynchronous(true);
    mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}

请注意onVsync参数中timestampNanos,这个值代表什么呢,其实代表的是Vsync信号到达App的时间,Vsync信号在通过socket通信发给App时候,会带上这个时间戳timestampNanos,这个过程其实是不会受主线程影响的。

在Vsync信号到来之后,onVsync方法没有被立刻调用,也可以产生Choreographer Skipped

二、写个Demo验证一下

代码语言:javascript复制
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {

    private TextView mTxtView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        mTxtView = findViewById(R.id.txt_view);
        mTxtView.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        mTxtView.setText("请求Vsync信号");//会触发scheduleTraversals,所以16ms以内会接受到Vsync信号
        try {
            Thread.sleep(1000);//这样子onVsync会推迟1000ms,才能被调用
        } catch (Exception e) {

        }
    }
}

注意我这里采用的是TextView,因为TextView点击没有UI刷新,所以不会触发scheduleTraversals,我在onClick中主动调用mTxtView.setText,会触发scheduleTraversals,所以App会在16ms以内会接受到Vsync信号,请注意16ms以内,时间不固定。

Vsync信号到来的时间点就是onVsync的形参timestampNanos。

然后mTxtView.setText完了之后再sleep 1000ms,处理完onClick代码。主线程会去处理onVsync的方法,由于Vsync信号早就到了,所以就算此时onVsync投放的Asynchronous的Message被立刻处理,但是已经晚了,所以还是会出现Choreographer Skipped。

代码语言:javascript复制
D KobeWang2: onClick : start
D KobeWang2: onClick : end
I Choreographer: Skipped 60 frames!  The application may be doing too much work on its main thread.

三、总结

其实有很多Demo可以产生Choreographer Skipped,但是不管你怎么写,肯定是下面两种场景之一。

3.1 场景一

[036]Choreographer Skipped真正含义里介绍的Demo,虽然Vsync信号到了,onVsync被及时调用,但是主线程中有未开始处理的耗时Message,推迟了doFrame的执行时间。

场景一

3.2 场景二

本文介绍的Demo,Vsync信号早早到了,但是由于主线程的耗时操作,onVsync无法被及时调用

场景二

3.3 更正

更正一下我在[036]Choreographer Skipped真正含义说的话

Choreographer Skipped真正反映的是onVsync和doFrame两个方法调用的时间间隔

修正为

Choreographer Skipped真正反映的是Vsync信号到达App的时间和doFrame方法调用的时间间隔

场景一和场景二,只不过是通过两种方式增大了这个时间间隔而已。

3.4 onVsync被调用

我无数次的提到onVsync被调用,那到底onVsync是怎么被调用的,其实主线程的Looper.loop中一次循环会先处理native层监听的vsync信号和Input事件,处理一次java层的Message,就是类似这样子的伪代码。

代码语言:javascript复制
public void loop() {
    for(;;) {
        //处理native层的任务,处理完所有vsync信号,input事件。
        //如果发现Vsync信号已经抵达APP,就会通过JNI回调onVsync方法
        doNativeTasks();
        //处理java层的Message,一次处理只能处理一个Message
        doJavaTasks();
    }
}

从主线程的Looper角度分析场景一和场景二的流程图如下:

充分展示了Vsync黄色块和doFrame紫色块之间的时间间隔是怎么被增大的。

尾巴

还记得神雕侠侣中找到情花毒解药的天竺神僧嘛,他先让自己中毒,才找到解药。所以我们在解决一些疑难BUG的时候,需要学会如何制造BUG,才能了解BUG产生的原理,才能找到解决BUG的方案。

0 人点赞