4

スケジュールされた時間に 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 のリソースを解放するだけでなくMediaPlayer.play()、サウンド キューが空になるまで OnCompletionListener を使用して連続呼び出しを行います。

カスタム完了フラグをチェックして、最初の呼び出しの直後に待機ループを配置しようとし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;
        }
    }
}
4

2 に答える 2

3

ウェイク ロックを適切に処理し、再生をスケジュールするために、CommonsWare の WakefulIntentService を使用しました。

それは適切な選択ではありません。

残念ながら、MediaPlayer.play() を呼び出した直後に IntentService のワーカー スレッドが終了し、MediaPlayer に登録されたリスナーは呼び出されません。

そのため、適切な選択ではありません。:-)

問題は、ワーカー スレッドを終了させる前に終了しないようにするにはどうすればよいかということです (つまり、処理が完了し、最後に onCompletion() メソッドが呼び出された場合)。

使用しないでくださいWakefulIntentService。使用しないでくださいIntentService。を使用しServiceます。自分で管理し、音声が終わったらサービスWakeLockを呼び出します。stopSelf()

于 2012-12-29T21:09:32.900 に答える