前言
在[036]Choreographer Skipped真正含義中阵子,我介紹了一種可以產(chǎn)生Choreographer Skipped的情況达址。就是在onVsync被調(diào)用之前,往主線程post的一個(gè)Message。那還有沒有其他方式可以產(chǎn)生這個(gè)Choreographer Skipped呢涧狮?
一避归、仔細(xì)看看代碼
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
...
//onVsync方法將會(huì)在Vsync信號(hào)接收之后被回調(diào)
//mTimestampNanos就是這次Vsync信號(hào)接收的時(shí)間
mTimestampNanos = timestampNanos;
mFrame = frame;
//往主線程的Looper中投放一個(gè)Asynchronous的Message,callback為this
//這個(gè)Message被處理的時(shí)候就會(huì)調(diào)用下面run-doFrame的方法
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
請(qǐng)注意onVsync參數(shù)中timestampNanos秃臣,這個(gè)值代表什么呢,其實(shí)代表的是Vsync信號(hào)到達(dá)App的時(shí)間哪工,Vsync信號(hào)在通過socket通信發(fā)給App時(shí)候奥此,會(huì)帶上這個(gè)時(shí)間戳timestampNanos,這個(gè)過程其實(shí)是不會(huì)受主線程影響的雁比。
在Vsync信號(hào)到來之后稚虎,onVsync方法沒有被立刻調(diào)用,也可以產(chǎn)生Choreographer Skipped
二偎捎、寫個(gè)Demo驗(yàn)證一下
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("請(qǐng)求Vsync信號(hào)");//會(huì)觸發(fā)scheduleTraversals蠢终,所以16ms以內(nèi)會(huì)接受到Vsync信號(hào)
try {
Thread.sleep(1000);//這樣子onVsync會(huì)推遲1000ms,才能被調(diào)用
} catch (Exception e) {
}
}
}
注意我這里采用的是TextView茴她,因?yàn)門extView點(diǎn)擊沒有UI刷新寻拂,所以不會(huì)觸發(fā)scheduleTraversals,我在onClick中主動(dòng)調(diào)用mTxtView.setText败京,會(huì)觸發(fā)scheduleTraversals兜喻,所以App會(huì)在16ms以內(nèi)會(huì)接受到Vsync信號(hào),請(qǐng)注意16ms以內(nèi)赡麦,時(shí)間不固定朴皆。
Vsync信號(hào)到來的時(shí)間點(diǎn)就是onVsync的形參timestampNanos帕识。
然后mTxtView.setText完了之后再sleep 1000ms,處理完onClick代碼遂铡。主線程會(huì)去處理onVsync的方法肮疗,由于Vsync信號(hào)早就到了,所以就算此時(shí)onVsync投放的Asynchronous的Message被立刻處理扒接,但是已經(jīng)晚了伪货,所以還是會(huì)出現(xiàn)Choreographer Skipped。
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.
三钾怔、總結(jié)
其實(shí)有很多Demo可以產(chǎn)生Choreographer Skipped碱呼,但是不管你怎么寫,肯定是下面兩種場景之一宗侦。
3.1 場景一
[036]Choreographer Skipped真正含義里介紹的Demo愚臀,雖然Vsync信號(hào)到了,onVsync被及時(shí)調(diào)用矾利,但是主線程中有未開始處理的耗時(shí)Message姑裂,推遲了doFrame的執(zhí)行時(shí)間。
3.2 場景二
本文介紹的Demo男旗,Vsync信號(hào)早早到了舶斧,但是由于主線程的耗時(shí)操作,onVsync無法被及時(shí)調(diào)用
3.3 更正
更正一下我在[036]Choreographer Skipped真正含義說的話
Choreographer Skipped真正反映的是onVsync和doFrame兩個(gè)方法調(diào)用的時(shí)間間隔
修正為
Choreographer Skipped真正反映的是Vsync信號(hào)到達(dá)App的時(shí)間和doFrame方法調(diào)用的時(shí)間間隔
場景一和場景二察皇,只不過是通過兩種方式增大了這個(gè)時(shí)間間隔而已茴厉。
3.4 onVsync被調(diào)用
我無數(shù)次的提到onVsync被調(diào)用,那到底o(hù)nVsync是怎么被調(diào)用的让网,其實(shí)主線程的Looper.loop中一次循環(huán)會(huì)先處理native層監(jiān)聽的vsync信號(hào)和Input事件呀忧,處理一次java層的Message师痕,就是類似這樣子的偽代碼溃睹。
public void loop() {
for(;;) {
//處理native層的任務(wù),處理完所有vsync信號(hào)胰坟,input事件因篇。
//如果發(fā)現(xiàn)Vsync信號(hào)已經(jīng)抵達(dá)APP,就會(huì)通過JNI回調(diào)onVsync方法
doNativeTasks();
//處理java層的Message笔横,一次處理只能處理一個(gè)Message
doJavaTasks();
}
}
從主線程的Looper角度分析場景一和場景二的流程圖如下:
充分展示了Vsync黃色塊和doFrame紫色塊之間的時(shí)間間隔是怎么被增大的竞滓。
尾巴
還記得神雕俠侶中找到情花毒解藥的天竺神僧嘛,他先讓自己中毒吹缔,才找到解藥商佑。所以我們?cè)诮鉀Q一些疑難BUG的時(shí)候,需要學(xué)會(huì)如何制造BUG厢塘,才能了解BUG產(chǎn)生的原理茶没,才能找到解決BUG的方案肌幽。