How to use a MediaPlayer Singleton

1.8k views Asked by At

I am new to Android developing and am starting with a simple soundboard application. I started developing a soundboard using multiple fragments until I realized that I was using multiple instances of MediaPlayer. This is not good because I want only one sound to play at a time.

I realized that I'd have to use a MediaPlayer Singleton to solve my problem. The only problem is that I can't find many sources or examples of the MediaPlayer Singleton online.

Here's what I originally put into every "onCreateView" in each fragment:

public static class FragmentPage1 extends Fragment {

    int selectedSoundId;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_page1, container, false);


        final MediaPlayer player = new MediaPlayer();
        final Resources res = getResources();

        final int[] buttonIds = { R.id.btn1, R.id.btn2, R.id.btn3, R.id.btn4, R.id.btn5, R.id.btn6, R.id.btn7, R.id.btn8, R.id.btn9 };
        final int[] soundIds = { R.raw.sound01, R.raw.sound02, R.raw.sound03, R.raw.sound04, R.raw.sound05, R.raw.sound06, R.raw.sound07, R.raw.sound08, R.raw.sound09 };

        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                for (int i = 0; i < buttonIds.length; i++) {
                    if (v.getId() == buttonIds[i]) {
                        selectedSoundId = soundIds[i];
                        AssetFileDescriptor afd = res.openRawResourceFd(soundIds[i]);
                        player.reset();
                        try {
                            player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        } catch (IllegalStateException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        try {
                            player.prepare();
                        } catch (IllegalStateException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        player.start();
                        break;
                    }
                }
            }
        };

        for (int i = 0; i < buttonIds.length; i++) {
            ImageButton soundButton = (ImageButton) rootView.findViewById(buttonIds[i]);
            registerForContextMenu(soundButton);
            soundButton.setOnClickListener(listener);
        }
        return rootView;
    }
}

To my knowledge I'd probably put the onClickListener inside of each fragment and the MediaPlayer Singleton in a new Java class. I don't know what to do from there though.

How do I implement a MediaPlayer Singleton and how do I call it back in the fragment's "onCreateView" method?

Examples are highly appreciated and thanks!

1

There are 1 answers

1
E. Fernandes On BEST ANSWER

See, Singleton is a design pattern, and it is implemented by setting the default constructor as private, then you should provide a get method from wich you can recover your object instance. Check out the example bellow:

public class Foo {
    private MediaPlaye md;

    private Foo () {
        md = new MediaPlayer();
    }

    public MediaPlayer getMediaPlayer () {
        if (md == null) {
            new Foo();
        }
        return md;
    }
}

In your sittuation, the best thing to do is to create a Service class that will encapsulate all the MediaPlayer methods. This is done like that because, usually, the developer wants that the player keeps playing even if the user leaves the Activity to which it is binded. In each fragment that you want to use the MediaPlayer API, you can bind the Service and use the defined interface. Take a look in the class below:

public class MusicPlayerService extends android.app.Service implements MediaPlayer.OnPreparedListener,
        MediaPlayer.OnErrorListener,
        MediaPlayer.OnCompletionListener,
        ObserverSubject {

    private static final int NOTIFY_ID = 1;
    private List<MusicPlayerObserver> mObservers;

    private MediaPlayer mMediaPlayer;
    private final IBinder playerBind = new MusicBinder();;

    private List<Track> mPlaylist;
    private Integer mPosition;

    private Boolean isRepeating;
    private Boolean isShuffling;
    private Boolean isPrepared;
    private Boolean isPaused;

    // Callback Methods______________________________________________
    @Override
    public void onCreate() {
        ...
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        ...
    }

    @Override
    public IBinder onBind(Intent intent) {
        return playerBind;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        mMediaPlayer.stop();
        mMediaPlayer.release();
        return false;
    }

    @Override
    public void onDestroy() {
        stopForeground(true);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        mp.reset();
        return false;
    }


    // UTIL METHODS__________________________________________________
    private Long getCurrentTrackId() {
        return mPlaylist.get(mPosition).getTrackId();
    }

    private Long getCurrentAlbumId() {
        return mPlaylist.get(mPosition).getAlbumId();
    }



    // MEDIA PLAYER INTERFACE________________________________________

    public void play() {
        ...
    }

    public void pause() {
        ...
    }

    public void resume() {
        ...
    }

    public void next() {
        ...
    }

    public void previous() {
        ...
    }

    public void seekTo(int pos) {
        ...
    }

    // SERVICE INTERFACE PROVIDER_____________________________________
    /**
     * Interface through the component bound to this service can interact with it
     */
    public class MusicBinder extends Binder {
        public MusicPlayerService getService() {
            return MusicPlayerService.this;
        }
    }
}

I highly recommend that you follow this strategy of creating a MusicPlayer service. Also, I suggest you to take a look in another Design Patter called Observer. Usually, in music apps, you want to update several UI elements based on the MP state. Observer is perfect for that situation.

Hope I've helped a little.