Android ToneGenerator在第一次调用时启动音频非常缓慢

4

我正在重复调用ToneGenerator.startTone()来发出短暂的声音。但在第一次调用时,它会阻塞很长时间。因此,第一个爆发声音的时间太长了。以下是一个示例:

成员变量:

private ToneGenerator mDTMFPlayer

在构造函数中:
mDTMFPlayer = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, TONE_RELATIVE_VOLUME);

在由OnClickListener.onClick()启动的Thread中:
long startTime = System.currentTimeMillis();
mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
Log.d(TAG,"After 1st: " + (System.currentTimeMillis() - startTime));
try { Thread.sleep(160); } catch (InterruptedException e) { }
mDTMFPlayer.stopTone();

startTime = System.currentTimeMillis();
mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
Log.d(TAG,"After 2nd: " + (System.currentTimeMillis() - startTime));
try { Thread.sleep(160); } catch (InterruptedException e) { }
mDTMFPlayer.stopTone();

startTime = System.currentTimeMillis();
mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
Log.d(TAG,"After 3rd: " + (System.currentTimeMillis() - startTime));
try { Thread.sleep(160); } catch (InterruptedException e) { }
mDTMFPlayer.stopTone();

以下是输出结果,其中startTone()的执行时间以毫秒为单位:
11-16 18:07:35.885 16927-17977/com.my.project D/Ring: After 1st: 454
11-16 18:07:36.502 16927-17977/com.my.project D/Ring: After 2nd: 0
11-16 18:07:36.672 16927-17977/com.my.project D/Ring: After 3rd: 1

第一次调用阻塞了近半秒钟,这对我来说太长了。之后的任何调用都会让阻塞消失一段时间。奇怪的是,如果我稍等片刻然后再试一次,它又变得缓慢了。似乎存在一个时间段,之后阻塞会重新出现。
请给予建议。

“一点儿”有多长时间?也就是说,这是一个进程问题,即“一点儿”是在您的进程终止后,并且通过用户操作重新创建后吗? - CommonsWare
不,流程继续,我等待不到一秒钟,然后再次点击按钮,调用onClick()并启动一个单独的线程(我不想在GUI线程上阻塞),再次调用startTone()。 - Emmanuel
1
好的,startTone() 只是调用了一个 native 方法(请参见源代码),所以我不确定你能做什么。 - CommonsWare
1
如果这不是本地的,我可能已经找到原因了。 :) - Emmanuel
2
点。抱歉如果我的评论让人产生了其他印象——实际上,我的评论更多是为了下一个遇到这个问题的人,如果你没有得到答案。 - CommonsWare
不用担心,让它自己去吧。 - Emmanuel
1个回答

1
我认为 AudioManager.STREAM_VOICE_CALL 导致了这个问题。我的设备行为与你的类似。当我运行应用程序时,第一次调用startTone()需要长时间初始化。如果我退出并重新进入应用程序,则所有 3 次调用都可以快速完成。但如果在应用程序启动前播放某些系统声音,则会显示相同的“慢、快、快”结果。
因此,我认为它与流切换/阻塞有关,因为在我的设备上使用 AudioManager.STREAM_NOTIFICATION 只需要 4-10 毫秒。您还可以在这里阅读更多信息:AudioManager 的流类型在低级别上有什么区别? 考虑以下代码:
for (int i = -1; i < 10; i++) {
    System.out.println("AudioSystem stream " + i);
    mDTMFPlayer = new ToneGenerator(i, TONE_RELATIVE_VOLUME);
    long startTime = System.currentTimeMillis();
    mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
    Log.d(TAG, "After 1st: " + (System.currentTimeMillis() - startTime));
    try {Thread.sleep(160);} catch (InterruptedException e) {}
    mDTMFPlayer.stopTone();

    startTime = System.currentTimeMillis();
    mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
    Log.d(TAG, "After 2nd: " + (System.currentTimeMillis() - startTime));
    try {Thread.sleep(160);} catch (InterruptedException e) {}
    mDTMFPlayer.stopTone();

    startTime = System.currentTimeMillis();
    mDTMFPlayer.startTone(ToneGenerator.TONE_DTMF_0);
    Log.d(TAG, "After 3rd: " + (System.currentTimeMillis() - startTime));
    try {Thread.sleep(160);} catch (InterruptedException e) {}
    mDTMFPlayer.stopTone();
    mDTMFPlayer.release();
}

输出:

I/System.out: AudioSystem stream -1 STREAM_DEFAULT
D/com.example.MainActivity: After 1st: 8
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 0 STREAM_VOICE_CALL
D/com.example.MainActivity: After 1st: 325
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 1 STREAM_SYSTEM
D/com.example.MainActivity: After 1st: 17
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 3
I/System.out: AudioSystem stream 2 STREAM_RING
D/com.example.MainActivity: After 1st: 28
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 3 STREAM_MUSIC
D/com.example.MainActivity: After 1st: 19
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 4 STREAM_ALARM
D/com.example.MainActivity: After 1st: 28
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 5 STREAM_NOTIFICATION
D/com.example.MainActivity: After 1st: 16
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 6 STREAM_BLUETOOTH_SCO
D/com.example.MainActivity: After 1st: 332
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 7 STREAM_SYSTEM_ENFORCED
D/com.example.MainActivity: After 1st: 324
D/com.example.MainActivity: After 2nd: 1
D/com.example.MainActivity: After 3rd: 1
I/System.out: AudioSystem stream 8 STREAM_DTMF
D/com.example.MainActivity: After 1st: 26
D/com.example.MainActivity: After 2nd: 2
D/com.example.MainActivity: After 3rd: 4
I/System.out: AudioSystem stream 9 STREAM_TTS
D/com.example.MainActivity: After 1st: 12
D/com.example.MainActivity: After 2nd: 4
D/com.example.MainActivity: After 3rd: 2  

顺便提一下,如果你想研究相关的C源代码,可以查看android_media_ToneGenerator.cpp, ToneGenerator.h, ToneGenerator.cpp AudioService.java


最终我使用了专门用于此目的的STREAM_DTMF,完全没有延迟。完美的解决方案。感谢您的回答。一定会给您悬赏。 - Emmanuel
启动音的持续时间应该是多久?50毫秒?150毫秒?默认/标准是什么? - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接