Android MediaPlayer - setDataSource and Release - IllegalStateException

user3534643 picture user3534643 · Mar 16, 2015 · Viewed 19k times · Source

I wrote my own MediaPlayer class to play files at a specific path and to play files from the assets folder. Here is the class:

public class CMediaPlayer extends MediaPlayer{

public void play(String audioPath){

   this.setOnCompletionListener(new OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            mp.release();
        }
    });

    File f = new File(audioPath);
    if(f.exists()){
        try{
            FileInputStream fis = new FileInputStream(f);
            FileDescriptor fileD = fis.getFD();
            this.setDataSource(fileD);
            this.prepare();

        }catch(IOException e){

        }
       this.start();
}
}

    public void play(AssetFileDescriptor descriptor){

        this.setOnCompletionListener(new OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
               mp.release();
            }
        });
        try {
            this.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength());
            descriptor.close();
            this.prepare();
        }catch (IOException e){

        }
        this.start();
    }

}

I want to play several sounds with that class from an activity. Here is my code:

public class playGame extends Activity {

//a lot of variables

    CMediaPlayer mediaPlayer; //declare my mediaplayer

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK ) {
            //release??????
            Intent myIntent = new Intent(getBaseContext(), startView.class);
            startActivity(myIntent);
        }
        return super.onKeyDown(keyCode, event);
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_play_question2);

        mediaPlayer = new CMediaPlayer(); //define my mediaplayer

       //stuff
    }

    //more variables

    public void playQuestion(File question){

        //stuff

        TextView myTextView = (TextView) findViewById(R.id.textViewQuestion);
        //stuff
        myTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            mediaPlayer.play(pathSoundQuestion); //play sound when clicked
            }
        });

    //stuff

        myImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               mediaPlayer.play(pathSoundQuestion); //play sound when clicked
            }
        });

        //stuff
        mediaPlayer.play(pathSoundQuestion); //plays sound immediatly, first played sound (works fine)

        //button1
        Button myButton = (Button) findViewById(R.id.button1);
        //stuff
        myButton.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(lastClickedButton == v){
                   //stuff
                    return;
                }
                //stuff
                mediaPlayer.play(pathAudio1); //play sound when clicked (error)
            }
        });

        //button2
        myButton = (Button) findViewById(R.id.button2);
       //stuff
        myButton.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(lastClickedButton == v){
                    //stuff
                    return;
                }
                //stuff
                mediaPlayer.play(pathAudio2); //play sound, same problem
            }
        });

       //goes on like this some more times...
    }

    public void logIn(View v, String right){
        //stuff

        if(right.equals("true")){
           //stuff
            try {
                AssetFileDescriptor descriptor = getAssets().openFd("Right");
                mediaPlayer.play(descriptor); //play sound from assets
                Intent myIntent = new Intent(getApplication(), playGame.class);
                startActivity(myIntent);
            }catch (IOException e){
            }
        }else{
          //stuff
            try {
                AssetFileDescriptor descriptor = getAssets().openFd("Wrong");
                mediaPlayer.play(descriptor); //play sound from assets
                Intent myIntent = new Intent(getApplication(), playGame.class);
                startActivity(myIntent);
            }catch (IOException e){
            }
        }
    }
}

The app plays the first sound as mentioned in my comments in my code. When I click a button which should start another sound I get the following error:

03-16 23:07:38.478  13646-13646/com.example.cello.myownquiz E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.IllegalStateException
            at android.media.MediaPlayer.setDataSource(Native Method)
            at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1005)

Is my MediaPlayer class okay or am I missing something depending on the state? Is this class the only spot to call release or do I have to put it somewhere in my activity, too?

Does anybody see my mistake? This problem with the mediaPlayer took me a whole day of trying, hope you can help me...

Answer

Amir picture Amir · Mar 17, 2015

change mp.release(); to mp.reset();

public void reset ()

Resets the MediaPlayer to its uninitialized state. After calling this method, you will have to initialize it again by setting the data source and calling prepare().

public void release ()

Releases resources associated with this MediaPlayer object. It is considered good practice to call this method when you're done using the MediaPlayer. In particular, whenever an Activity of an application is paused (its onPause() method is called), or stopped (its onStop() method is called), this method should be invoked to release the MediaPlayer object, unless the application has a special need to keep the object around. In addition to unnecessary resources (such as memory and instances of codecs) being held, failure to call this method immediately if a MediaPlayer object is no longer needed may also lead to continuous battery consumption for mobile devices, and playback failure for other applications if no multiple instances of the same codec are supported on a device. Even if multiple instances of the same codec are supported, some performance degradation may be expected when unnecessary multiple instances are used at the same time.

You need to keep the object around.

You can do it in a simple way

        MediaPlayer mediaPlayer = new MediaPlayer();
        mediaPlayer.setDataSource(context, ringtone);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MEDIA);
        mediaPlayer.prepare();
        mediaPlayer.start();