我正在尝试制作一个应用程序,该应用程序在预定时间使用 MediaPlayer 播放一系列声音。为了正确处理唤醒锁定并安排播放,我使用了CommonsWare 的 WakefulIntentService。
不幸的是,IntentService 的工作线程在我调用后立即退出,MediaPlayer.play()
并且没有调用 MediaPlayer 注册的侦听器。相反,会记录异常:
W/MessageQueue(6727): Handler (android.media.MediaPlayer$EventHandler) {4160d820} sending message to a Handler on a dead thread
W/MessageQueue(6727): java.lang.RuntimeException: Handler (android.media.MediaPlayer$EventHandler) {4160d820} sending message to a Handler on a dead thread
W/MessageQueue(6727): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:294)
W/MessageQueue(6727): at android.os.Handler.sendMessageAtTime(Handler.java:473)
W/MessageQueue(6727): at android.os.Handler.sendMessageDelayed(Handler.java:446)
W/MessageQueue(6727): at android.os.Handler.sendMessage(Handler.java:383)
W/MessageQueue(6727): at android.media.MediaPlayer.postEventFromNative(MediaPlayer.java:2063)
W/MessageQueue(6727): at dalvik.system.NativeStart.run(Native Method)
据我了解,这是由于 MediaPlayer 完成时工作线程已经死亡引起的。如果我通过调试器暂停线程并让播放器完成,一切正常。
在我的监听器中,我不仅释放 MediaPlayer 的资源,还使用 OnCompletionListener 进行连续MediaPlayer.play()
调用,直到声音队列为空。
我尝试在初始调用之后立即放置一个等待循环play()
,检查自定义完成标志,但它似乎冻结了,因为 MediaPlayer 的回调是在调用同一个线程play()
上调用的。
问题是,我怎样才能让工作线程在我让它退出之前不退出(即已经处理过并且onCompletion()
最后一次调用了该方法?
这是我的服务代码:
public class SoundService extends WakefulIntentService {
private static final TAG = "SoundService";
private final Queue<SoundDescriptor> soundQueue = new ConcurrentLinkedQueue<SoundDescriptor>();
private final OnCompletionListener onComediaPlayerletionListener = new OnComediaPlayerletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.reset();
try {
if (!playNextFromQueue(mediaPlayer)) {
Log.v(TAG, "Reached end of queue. Cleaning up.");
release();
}
} catch (Exception e) {
Log.e(TAG, "Exception!", e)
release();
}
}
};
private final OnErrorListener onErrorListener = new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
Log.v(TAG, "Error!!");
release();
return false;
}
};
public SoundService() {
// populate soundQueue
}
protected void doWakefulWork(Intent intent) {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnComediaPlayerletionListener(onComediaPlayerletionListener);
mediaPlayer.setOnErrorListener(onErrorListener);
playNextFromQueue(mediaPlayer);
}
private boolean playNextFromQueue(MediaPlayer mediaPlayer) throws IllegalArgumentException, IllegalStateException, IOException {
SoundDescriptor descriptor = soundQueue.poll();
if (descriptor != null) {
mediaPlayer.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength());
descriptor.close();
mediaPlayer.prepare();
mediaPlayer.start();
return true;
} else {
return false;
}
}
}