如何使WakefulService(IntentService)等到MediaPlayer完成?

时间:2012-12-29 20:56:36

标签: android media-player intentservice

我正在尝试制作一个应用程序,它会在预定的时间使用MediaPlayer播放一系列声音。为了正确处理唤醒锁定并安排回放,我使用了CommonsWare's 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()调用之后立即放置一个等待循环,检查自定义完成标志,但它似乎冻结,因为在调用同一线程play()时调用了MediaPlayer的回调。 / p>

问题是,在我允许之前,如何让工作线程不退出(即已经处理并且最后一次调用了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;
        }
    }
}

2 个答案:

答案 0 :(得分:3)

  

为了正确处理唤醒锁并安排播放,我使用了CommonsWare的WakefulIntentService。

这不是一个合适的选择。

  

不幸的是,IntentService的工作线程在我调用MediaPlayer.play()后立即退出,并且没有调用MediaPlayer注册的侦听器。

这就是为什么它不是一个合适的选择。 : - )

  

问题是,在我允许之前,如何让工作线程不退出(即已经处理并且最后一次调用了onCompletion()方法?

请勿使用WakefulIntentService。不要使用IntentService。使用Service。自己管理WakeLock,并在音频播放完毕后在服务上调用stopSelf()

答案 1 :(得分:2)

MediaPlayer的问题在于它在实例化时将Handler缓存到当前线程。如果当前线程没有Looper,则默认为主线程。它将使用此处理程序来执行侦听器回调。

让我们假设您实例化播放器并在服务的线程中使用它。如果你通过服务调用播放器方法,然后这个服务线程终止,播放器将尝试使用指向已故线程的处理程序调用已注册侦听器的回调,并且它将抛出问题中显示的异常。 / p>

我发现MediaPlayer非常棘手且不可靠。最简单的解决方案是在主线程中使用播放器。他们是短暂的电话,不应该提出ANR。