我今天一整天都在尝试解决这个问题,而在其他日子里,我花了太多时间。但今天我至少能够细化这个问题。
我有一个带有错误标题的 .acc 音频文件,我们称之为"BadFile"
:根据我前几天注意到的来自 android 本机 C 代码的 logcat 转储,标题说文件有 44.100Hz,但数据本身有 0Hz。
我有一个代表播放列表的文件数组列表和一个定义在给定时刻播放的歌曲的 int 指针。当我在播放列表中排队播放一些歌曲时,比如 GoodFile1、GoodFile2、BadFile、GoodFile3、GoodFile4 等,指针设置为 0,GoodFile1 开始播放。
//这是从 Thread.currentThread().getId() == 1 调用的
private void play() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
playCicle();
}
});
t.start();
}
private synchronized void playCicle() {
Log.e("THREAD" , "T:" + Thread.currentThread().getId());
Log.e("TRYING TO PLAY", playlist.get(playPointer).getFile().getAbsolutePath());
try {
if(progressSenderRunnable != null) {
progressSenderRunnable.stop();
}
if(myMP != null) {
myMP.release();
}
myMP = new MediaPlayer();
myMP.setAudioStreamType(AudioManager.STREAM_MUSIC);
myMP.setDataSource(mService, Uri.parse(Uri.encode(playlist.get(playPointer).getFile().getAbsolutePath())));
setOnPrepareListener();
myMP.prepare();
setOnCompletionListener();
setOnErrorListener();
myMP.start();
sendImPlaying();
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
} catch (SecurityException e) {
e.printStackTrace();
return;
} catch (IllegalStateException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
Log.e("METHOD " , "EXITING");
}
private void setOnCompletionListener() {
myMP.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer arg0) {
Log.e("onCompletionthread" , "T: "+Thread.currentThread().getId());
Thread t = new Thread(new Runnable() {
@Override
public void run() {
if(!userSetPlayPointerNext && !userSetPlayPointerPrevious) {
playPointer++;
}
userSetPlayPointerNext = false;
userSetPlayPointerPrevious = false;
if(playPointer < playlist.size()) {
play();
}
else {
if(progressSenderRunnable != null) {
progressSenderRunnable.stop();
}
if(myMP != null) {
myMP.release();
}
sendImStopped();
}
}
});
t.start();
}
});
}
当它开始播放第一首歌曲时,我单击下一个按钮,从而导致 ID 为 1 的线程调用下一个方法。
void playNextSong() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
if(isPlaying() && playPointer < playlist.size() - 1) {
playPointer++;
userSetPlayPointerNext = true;
myMP.seekTo(myMP.getDuration() - 1);
}
}
});
t.start();
}
GoodFile2 开始播放。然后我开始向下一个按钮发送垃圾邮件,用户界面就挂起。在这个挂起之后,我可以很好地向下一个按钮发送垃圾邮件(因为我们已经通过了 BadFile)。
10-17 21:18:23.734: E/THREAD(24021): T:41
10-17 21:18:23.734: E/TRYING TO PLAY(24021): /mnt/sdcard/Music/BadFileTest/01_Compay Segundo - Te Apartes De Mi.mp3
10-17 21:18:23.744: E/METHOD(24021): EXITING
10-17 21:18:23.744: E/PREPARED(24021): PREPARED
10-17 21:18:24.364: E/Service rcv(24021): 17
10-17 21:18:24.364: E/back in handler(24021): 1
10-17 21:18:24.384: E/onCompletionthread(24021): T: 1
10-17 21:18:24.384: E/THREAD(24021): T:45
10-17 21:18:24.384: E/TRYING TO PLAY(24021): /mnt/sdcard/Music/BadFileTest/02 - Blue.aac
10-17 21:18:24.394: E/METHOD(24021): EXITING
10-17 21:18:24.724: E/PREPARED(24021): PREPARED
10-17 21:18:28.914: E/onCompletionthread(24021): T: 1
10-17 21:18:28.924: E/THREAD(24021): T:48
10-17 21:18:28.924: E/TRYING TO PLAY(24021): /mnt/sdcard/Music/BadFileTest/02_Ibrahím Ferrer - Ay Candela.mp3
10-17 21:18:28.944: E/METHOD(24021): EXITING
10-17 21:18:29.074: E/PREPARED(24021): PREPARED
在:10-17 21:18:23.734 你可以看到 GoodFile1 几乎被 id 为 41 的线程播放
在:10-17 21:18:24.364:您可以看到服务正在接收playNextSong()
.
在:10-17 21:18:24.364:: 可以看到主线程从调用返回playNextSong()
与此同时,id 为 45 的线程正在加载 BadFile 并退出该playNextSong()
方法。
现在这里是挂起:
10-17 21:18:24.724:电子/准备(24021):准备
10-17 21:18:28.914: E/onCompletionthread(24021): T: 1
在这 4 秒内,我的 UI 无法使用。我看到的是,即使没有从主线程调用 setOnCompletion 侦听器,它也是在侦听器调用时调用的主线程,尽管我不确定这是否与挂起有关。
目前我可能有太多不必要的线程,但我试图尽可能远离主线程完成大部分工作。我将来会改变这个。
我认为目前同步关键字也没用,因为我使用完成侦听器来启动新线程来播放下一首歌曲。
如果有人知道我做错了什么让我支付那些 4s,我将不胜感激!
编辑:
找到我正在谈论的控制台转储,此警告不会显示在我的包 logcat 过滤器中:
10-18 07:46:37.055: E/THREAD(10772): T:21
10-18 07:46:37.055: E/TRYING TO PLAY(10772): /mnt/sdcard/Music/BadFileTest/02 - Blue.aac
10-18 07:46:37.075: I/StagefrightPlayer(157): setDataSource('/mnt/sdcard/Music/BadFileTest/02 - Blue.aac')
10-18 07:46:37.625: V/OMXCodec(157): Attempting to allocate OMX node 'OMX.Nvidia.aac.decoder'
10-18 07:46:37.625: V/OMXCodec(157): Attempting to allocate OMX node 'OMX.TI.AAC.decode'
10-18 07:46:37.625: V/OMXCodec(157): Successfully allocated software codec 'AACDecoder'
10-18 07:46:37.625: E/PREPARED(10772): PREPARED
10-18 07:46:37.625: E/start(10772): before
10-18 07:46:37.625: W/AACDecoder(157): Sample rate was 44100 Hz, but now is 0 Hz
10-18 07:46:37.625: E/start(10772): after
10-18 07:46:37.635: W/AACDecoder(157): Disable AAC+/eAAC+ since upsampling factor is 1
10-18 07:46:37.635: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.635: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 30, substituting silence
10-18 07:46:37.645: W/AACDecoder(157): AAC decoder returned error 20, substituting silence
奇怪的是,这个编解码器错误警告持续了大约 4 秒,只是我的主线程挂起的时间。