简体   繁体   English

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

[英]How to make WakefulService (IntentService) wait until MediaPlayer finishes?

I'm trying to make an app, which plays series of sounds using MediaPlayer, at scheduled times. 我正在尝试制作一个应用程序,它会在预定的时间使用MediaPlayer播放一系列声音。 To properly handle the wake lock and schedule the playback I used CommonsWare's WakefulIntentService . 为了正确处理唤醒锁定并安排播放,我使用了CommonsWare的WakefulIntentService

Unfortunately, the IntentService's worker thread quits right after I call MediaPlayer.play() and neither MediaPlayer registered listeners are called. 不幸的是,IntentService的工作线程在我调用MediaPlayer.play()后立即退出,并且没有调用MediaPlayer注册的侦听器。 Instead, the exception is logged: 而是记录异常:

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)

As far as I understand, it is caused by the worker thread being already dead when MediaPlayer completes. 据我所知,它是由MediaPlayer完成时工作线程已经死亡引起的。 If I pause the thread by means of the debugger and let the player complete, everything works fine. 如果我通过调试器暂停线程并让播放器完成,一切正常。

In my listeners I not only release MediaPlayer's resources, but also use OnCompletionListener to do consecutive MediaPlayer.play() calls until the sound queue is empty. 在我的监听器中,我不仅释放MediaPlayer的资源,还使用OnCompletionListener进行连续的MediaPlayer.play()调用,直到声音队列为空。

I tried putting a wait loop right after the initial play() call, checking for a custom completion flag, but it seems to freeze because MediaPlayer's callbacks are called on the same thread play() was called. 我尝试在初始play()调用之后放置一个等待循环,检查自定义完成标志,但它似乎冻结,因为MediaPlayer的回调是在相同的线程调用play()被调用的。

The question is, how can I make the worker thread not quit before I let it do so (ie the has been processed and the onCompletion() method has been called for the last time? 问题是,在我允许之前,如何让工作线程不退出(即已经处理并且最后一次调用onCompletion()方法?

Here is the code of my service: 这是我的服务代码:

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;
        }
    }
}

To properly handle the wake lock and schedule the playback I used CommonsWare's WakefulIntentService. 为了正确处理唤醒锁定并安排播放,我使用了CommonsWare的WakefulIntentService。

That's not an appropriate choice. 这不是一个合适的选择。

Unfortunately, the IntentService's worker thread quits right after I call MediaPlayer.play() and neither MediaPlayer registered listeners are called. 不幸的是,IntentService的工作线程在我调用MediaPlayer.play()后立即退出,并且没有调用MediaPlayer注册的侦听器。

That's why it's not an appropriate choice. 这就是为什么它不是一个合适的选择。 :-) :-)

The question is, how can I make the worker thread not quit before I let it do so (ie the has been processed and the onCompletion() method has been called for the last time? 问题是,在我允许之前,如何让工作线程不退出(即已经处理并且最后一次调用onCompletion()方法?

Don't use WakefulIntentService . 不要使用WakefulIntentService Don't use IntentService . 不要使用IntentService Use Service . 使用Service Manage the WakeLock yourself, and call stopSelf() on the service when the audio is finished. 自己管理WakeLock ,并在音频结束时在服务上调用stopSelf()

The problem with MediaPlayer is that it caches a Handler to the current thread when it is instantiated. MediaPlayer的问题在于它在实例化时将Handler缓存到当前线程。 In case the current thread has no Looper, it defaults to the main thread. 如果当前线程没有Looper,则默认为主线程。 It will use this handler to execute the listeners callbacks. 它将使用此处理程序来执行侦听器回调。

Lets say you instantiate the player and use it inside a service's thread. 让我们假设您实例化播放器并在服务的线程中使用它。 If you call the player methods through the service, and then this service thread dies, the player will try to call the callbacks to the registered listeners using a handler pointing to the deceased thread, and it will throw the exception shown in the question. 如果通过服务调用播放器方法,然后此服务线程终止,播放器将尝试使用指向已故线程的处理程序调用已注册侦听器的回调,并将抛出问题中显示的异常。

I've find MediaPlayer very tricky and unreliable. 我发现MediaPlayer非常棘手且不可靠。 The simplest solution is to use the player in the main thread. 最简单的解决方案是在主线程中使用播放器。 They are short calls, shouldn't raise ANR's. 他们是短暂的电话,不应该提出ANR。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何让IntentService等到BroadcastReceiver完成执行? - How to make an IntentService wait until BroadcastReceiver has finished executing? 如何让Android TestRunner等待活动结束 - How to make Android TestRunner wait until activity finishes 如何等待onActivityResult完成? - How to wait until onActivityResult finishes? 从WakefulService启动IntentService - Starting an IntentService form a WakefulService 等待另一种方法,直到协程完成 [Android Kotlin] - Make wait another method until coroutine finishes [Android Kotlin] 如何等待直到异步方法完成使用Future? - How to wait until an async method finishes using Futures? 如何等待直到异步任务中另一个类的方法在Android中完成 - How to wait until method from another class in asynctask finishes in android 等待直到su中的命令完成 - Wait until a command in su finishes Android MediaPlayer setDataSource会阻塞,直到另一个MediaPlayer实例完成准备工作为止 - Android MediaPlayer setDataSource blocks until another MediaPlayer instance finishes preparing 如何让MediaPlayer等待其他点击请求? - How to make wait MediaPlayer for other hit requests?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM