Tappa Tappa
Tappa Tappa

Reputation: 204

Play mp3 Android async. Plays but then crashes thread

I need to play a sound Asynchronously in my Android app.

It's a short beep mp3 (1 second long), but I still need it to not block the current thread at all... milliseconds matter, the thread is busy looking for new Bluetooth data.

The thread that needs to play the sound is an Intent Service.

I have the following code near the beginning of the function call in the main thread... this is to prepare the MP3 so it's ready to play later. I was hoping I could just initialise it like this and call some sort of "PlaySound" method whenever I need to.

My understanding was that by using the OnPreparedListener the MediaPlayer would take care of all the nuts and bolts of playing the sound in a different thread for me.

        MediaPlayer objMediaPlayer = MediaPlayer.create(getApplicationContext(),R.raw.beeplow);
        objMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                mp.start();
            }
        });

The above seems to work, no crash.

Then, later on I want to play the sound:

                    //play sound...
                    objMediaPlayer.prepareAsync();

And this DOES actually play the sound... but then, epic fail, the thread crashes.

What am I doing wrong?

Here is the LogCat info if that helps at all:

08-19 14:17:51.790  15995-16037/com.example.owner.soundtest V/MediaPlayer﹕ setVideoSurfaceTexture
08-19 14:17:51.790  15995-16037/com.example.owner.soundtest V/MediaPlayer﹕ prepareAsync
08-19 14:17:51.790  15995-16037/com.example.owner.soundtest E/MediaPlayer﹕ prepareAsync called in state 8
08-19 14:17:51.800  15995-16037/com.example.owner.soundtest I/MediaPlayer﹕ Info (973,0)
08-19 14:17:51.800  15995-16037/com.example.owner.soundtest I/MediaPlayer﹕ Don't send intent. msg.arg1 = 0, msg.arg2 = 0
08-19 14:17:51.800  15995-16037/com.example.owner.soundtest V/MediaPlayer-JNI﹕ start
08-19 14:17:51.800  15995-16037/com.example.owner.soundtest V/MediaPlayer﹕ start
08-19 14:17:52.260  15995-16008/com.example.owner.soundtest V/MediaPlayer﹕ message received msg=2, ext1=0, ext2=0
08-19 14:17:52.260  15995-16008/com.example.owner.soundtest V/MediaPlayer﹕ playback complete
08-19 14:17:52.260  15995-16008/com.example.owner.soundtest V/MediaPlayer﹕ callback application
08-19 14:17:52.270  15995-16008/com.example.owner.soundtest W/MessageQueue﹕ Handler (android.media.MediaPlayer$EventHandler) {42fb2890} sending message to a Handler on a dead thread
    java.lang.RuntimeException: Handler (android.media.MediaPlayer$EventHandler) {42fb2890} sending message to a Handler on a dead thread
            at android.os.MessageQueue.enqueueMessage(MessageQueue.java:311)
            at android.os.Handler.enqueueMessage(Handler.java:623)
            at android.os.Handler.sendMessageAtTime(Handler.java:592)
            at android.os.Handler.sendMessageDelayed(Handler.java:563)
            at android.os.Handler.sendMessage(Handler.java:500)
            at android.media.MediaPlayer.postEventFromNative(MediaPlayer.java:2776)
            at dalvik.system.NativeStart.run(Native Method)
08-19 14:17:52.270  15995-16008/com.example.owner.soundtest V/MediaPlayer﹕ back from callback

Further Info

Is it something to do with the "getApplicationContext()" I'm using... is that the right way to create the object? I am still really new to Android and all this Context and Intent stuff hasn't sunk in despite many hours of reading and research.

All the above code is called within a thread of an Intent Service... so it's completely disconnected from the UI already.

Hacky Fix

By doing this at the start of my thread:

        MediaPlayer objMediaPlayer = MediaPlayer.create(getBaseContext(),R.raw.beeplow);

And then later every time I want to play the short beep I do this:

                    if(objMediaPlayer.isPlaying()){
                        objMediaPlayer.stop();
                        objMediaPlayer.release();
                        objMediaPlayer = MediaPlayer.create(getBaseContext(),R.raw.beeplow);
                    }
                    objMediaPlayer.start();

Seems to work. God awful ugly code and certainly bad form, but yeah, I just want to play a $** sound without spending hours and hundreds of lines of code! Craxy.

Proper Fix ???

The below seems to work. Hopefully this is the proper way to do it... when I see other solutions out there, they seem to be 50+ lines of code buried in a service etc. etc. I like this one below... if this is the right way to be firing off a thread I'll be doing this everywhere!

                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            MediaPlayer objMediaPlayer = MediaPlayer.create(getBaseContext(),R.raw.beeplow);
                            objMediaPlayer.start();
                        }
                    }, "PlayBeepLow").start();

Upvotes: 1

Views: 1138

Answers (1)

Spurdow
Spurdow

Reputation: 2043

sending message to a Handler on a dead thread

Its not safe doing it in IntentService , as it stop itself when it runs out of work.

All the above code is called within a thread of an Intent Service... so it's completely disconnected from the UI already.

When all requests have been handled, the IntentService-onHandleIntent stops itself.

I suggest doing it in a Service , and handle your own thread

@Override
public int onStartCommand(final Intent intent, int flags, final int startId) 
{
    new Thread(new Runnable() {
        @Override
        public void run() {
            // handle your own media playing here...
        }
    }, "MplayerService").start();

    ...
}

and if you want users to control your media player

I suggest binding a Service will do here

Further info from the docs:

Service A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.

IntentService IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

hope that helps.

Upvotes: 1

Related Questions