Welcome to “Android Evolution”

preface

Android SDK provides 3 sets of audio playback apis, respectively: MediaPlayer, SoundPool, AudioTrack, this article focuses on MediaPlayer.

The Android multimedia framework supports playing a wide variety of common media types so that you can easily integrate audio, video, and pictures into your applications.

I. How to use MediaPlayer

You can use the MediaPlayer API to play audio or video from media files stored in application resources (raw resources), standalone files in the file system, or data streams obtained over a network connection.

MediaPlayer objects can fetch, decode, and play audio and video from a variety of different media sources.

1. How MediaPlayer loads audio

1.1. Static mode

MediaPlayer provides a static way to load audio files as follows

1.1.1. First method:

The resid parameter is generally the ID of the media file we store in the resource folder RES/RAW (which needs to be created by ourselves)

public static MediaPlayer create(Context context, int resid);
Copy the code

How does the following code play the audio provided as a local raw resource (stored in the application’s RES /raw/ directory)

MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you
    
Copy the code
1.1.2. The second method:

Parameter Uri, which can be used to point to a network resource or a local file.

public static MediaPlayer create(Context context, Uri uri);
Copy the code
1.2. Dynamic mode
1.2.1, setDataSource (String path)

Transmits and plays the content on the remote web site or the local SD card audio file address through HTTP streaming

// Sets the data source (file-path or http/rtsp URL) to use. //String url = "http://........" ; // Load music from network String url = ".. /music/samsara.mp3"; MediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();Copy the code
1.2.2 setDataSource(AssetFileDescriptor AFD) & setDataSource(FileDescriptor fd, Long offset, Long Length)

Play the audio files in the assets directory

setDataSource(AssetFileDescriptor afd)

AssetFileDescriptor fd = getAssets().openfd ("music.mp3"); MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.prepare() ;Copy the code

Note: setDataSource(AssetFileDescriptor AFD) Has requirements for API versions. The SDK version must be at least 24. You are advised to use the following method to play audio files in assets

setDataSource (FileDescriptor fd, long offset, long length)

AssetFileDescriptor fd = getAssets().openfd ("music.mp3"); MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(fd, fd.getStartOffset(), fd.getLegth()) mediaPlayer.prepare() ;Copy the code
1.2.3, setDataSource (Context Context, Uri Uri)

This method usually obtains the shared music provided by the Android system through the ContentProvider to obtain the URI, and then sets the data playback

Uri myUri = .... ; // initialize Uri here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();Copy the code
  • Conclusion:
  • 1. Static loading is simple, but each call creates a MediaPlayer object, suitable for one-off task scenarios.
  • 2, When using MediaPlayer to loop, it is not suitable to use static mode, can use dynamic loading mode, using a MediaPlayer object to loop load resource files.
  • The prepare() method must be called after dynamic loading

MediaPlayer audio playback package

1. Control methods of MediaPlayer

MediaPlayer has three main methods for controlling playback

  • Start () : starts or resumes playback
  • Stop () : Stops the playback
  • Pause () : Pauses the playback

2. MediaPlayer audio playback package

2.1 Play control of MediaPlayer

Create MediaManager class management MediaPlayer add play control method parameters: address to play audio & listen to play finished listening

public class MediaManager { public static MediaPlayer mMediaPlayer; private static boolean isPause; public void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener){ if(mMediaPlayer == null){ mMediaPlayer = new MediaPlayer(); }else{ mMediaPlayer.reset(); } try { mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setOnCompletionListener(onCompletionListener); mMediaPlayer.setDataSource(filePath); mMediaPlayer.prepare(); mMediaPlayer.start(); } catch (IOException e) { e.printStackTrace(); }}Copy the code
2.2 MediaPlayer state control
Public void pause(){if(mMediaPlayer! = null && mMediaPlayer.isPlaying()){ mMediaPlayer.pause(); isPause = true; }} public void resume(){if(mMediaPlayer! = null && isPause){ mMediaPlayer.start(); isPause = false; }}Copy the code
2.3 Release of MediaPlayer
  • MediaPlayer takes up valuable system resources.
  • Therefore, you should always take extra precautions to ensure that instances of MediaPlayer are not retained for too long.
  • After this is done, you should always call Release () to ensure that all system resources allocated to it are released correctly.
public void release(){
    if(mMediaPlayer != null){
        mMediaPlayer.release();
        mMediaPlayer = null;
    }
}
Copy the code

The complete code is as follows:

public  class MediaManager {

    public static MediaPlayer mMediaPlayer;
    private static boolean isPause;

    public  void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener){
        if(mMediaPlayer == null){
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    mMediaPlayer.reset();
                    return false;
                }
            });
        }else{
            mMediaPlayer.reset();
        }
        try {
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.setOnCompletionListener(onCompletionListener);
            mMediaPlayer.setDataSource(filePath);
            mMediaPlayer.prepare();
            mMediaPlayer.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void pause(){
        if(mMediaPlayer != null && mMediaPlayer.isPlaying()){
            mMediaPlayer.pause();
            isPause = true;
        }
    }

    public void resume(){
        if(mMediaPlayer != null && isPause){
            mMediaPlayer.start();
            isPause = false;
        }
    }

    public void release(){
        if(mMediaPlayer != null){
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }
}
Copy the code

MediaPlayer is initialized asynchronously

  • In principle, using MediaPlayer should be very simple. It’s important to note, however, that there are some additional things you need to do to properly integrate it with Android applications with formal audio playback requirements.
  • A call to prepare(), for example, can take a long time to execute because it may involve fetching and decoding media data.
  • Therefore, as with any method that may take a long time to execute, do not call it from the application’s interface thread.
  • Doing so can cause the interface to sometimes visibly pause until the system returns to the method, which is a very poor user experience and can lead to ANR (application non-response) errors.

Considering the complexity of the network situation and the long time it takes to fetch and decode data, it is not recommended to use prepare() to load the buffer during stream playback, especially not in the MAIN UI thread.

The MediaPlayer framework provides a convenient way to do this through the prepareAsync() method.

This method starts preparing the media in the background and returns immediately.

After when the media is ready, the system will call through setOnPreparedListener () configuration of the MediaPlayer. OnPreparedListener the onPrepared () method.

1. State management

When you write code that interacts with MediaPlayer objects, you can control them based on their internal state. State management as shown below:

  • 1. When you create a new MediaPlayer, it is in the Idle state.
  • 2. At this point, you should initialize the class by calling setDataSource() to make it in the “Initialized” state.
  • 3. You must then complete the preparation using the prepare() or prepareAsync() methods.
  • 4. When MediaPlayer is ready, it enters the “Prepared” state, which means you can make it play media by calling start().
  • 5. At this point, as shown in the figure, you can switch between the “Started”, “Paused”, and “PlaybackCompleted” states by calling the start(), Pause (), and seekTo() methods.
  • 6. Note, however, that when you call stop(), you cannot call start() again unless you prepare the MediaPlayer again.

2. Event monitoring

There are four common listening methods in MediaPlayer:

2.1. Prepare event listener: OnPreparedListener

The above mentioned system calls through setOnPreparedListener () configuration of the MediaPlayer. The onPrepared OnPreparedListener () method.

OnPreparedListener interface source:

/**
 * Interface definition for a callback to be invoked when the media
 * source is ready for playback.
 */
public interface OnPreparedListener
{
    /**
     * Called when the media file is ready for playback.
     *
     * @param mp the MediaPlayer that is ready for playback
     */
    void onPrepared(MediaPlayer mp);
}
Copy the code

Usage:

mMediaPlayer.setOnPreparedListener(this);
Copy the code
2.2. Complete the listening event: OnCompletionListener

Media files was over when the callback, the system will call by setOnCompletionListener () the MediaPlayer configuration. The OnCompletionListener onCompletion () method.

OnCompletionListener interface source:

/**
 * Interface definition for a callback to be invoked when playback of
 * a media source has completed.
 */
public interface OnCompletionListener
{
    /**
     * Called when the end of a media source is reached during playback.
     *
     * @param mp the MediaPlayer that reached the end of the file
     */
    void onCompletion(MediaPlayer mp);
}
Copy the code

Usage:

mMediaPlayer.setOnCompletionListener(this);
Copy the code
2.3, network origin loading state to monitor events: OnBufferingUpdateListener

Cache progress monitoring when loading network resource audio System will be called by setOnBufferingUpdateListener () configuration of the MediaPlayer. The OnBufferingUpdateListener onBufferingUpdate () method.

OnBufferingUpdateListener interface source code:

/** * Interface definition of a callback to be invoked indicating buffering * status of a media resource being streamed over the network. */ public interface OnBufferingUpdateListener { /** * Called to update status in buffering a media stream received through * progressive HTTP download. The received buffering percentage * indicates how much of the content has been buffered or played. * For example a buffering update of 80 percent when half the content * has already been played indicates that the next 30 percent of the * content to play has been buffered. * * @param mp the MediaPlayer  the update pertains to * @param percent the percentage (0-100) of the content * that has been buffered or played thus far */ void onBufferingUpdate(MediaPlayer mp, int percent); }Copy the code

Usage:

mMediaPlayer.setOnBufferingUpdateListener(this);
Copy the code
2.4. Error listener event: OnErrorListener

Callback when an error occurs during playback. , the system will call by setOnErrorListener () configuration of the MediaPlayer. OnErrorListener the onError () method.

OnErrorListener interface source:

/** * Interface definition of a callback to be invoked when there * has been an error during an asynchronous operation (other errors * will throw exceptions at method call time). */ public interface OnErrorListener { /** * Called to indicate an error. * * @param mp the MediaPlayer the error pertains to * @param what the type of error that has occurred: * <ul> * <li>{@link #MEDIA_ERROR_UNKNOWN} * <li>{@link #MEDIA_ERROR_SERVER_DIED} * </ul> * @param extra an extra code, specific to the error. Typically * implementation dependent. * <ul> * <li>{@link #MEDIA_ERROR_IO} * <li>{@link #MEDIA_ERROR_MALFORMED} * <li>{@link #MEDIA_ERROR_UNSUPPORTED} * <li>{@link #MEDIA_ERROR_TIMED_OUT} * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error. * </ul> * @return True if the method handled  the error, false if it didn't. * Returning false, or not having an OnErrorListener at all, will * cause the OnCompletionListener to be called. */ boolean onError(MediaPlayer mp, int what, int extra); }Copy the code

Usage:

mMediaPlayer.setOnErrorListener(this);
Copy the code

4. MediaPlayer Audio playback state management & Listening encapsulation

According to the above summary, MediaPlayer player status management & listening encapsulation, source code is as follows:

/**
 * 音频播放控制类
 */
public class Mp3Player implements OnPreparedListener, OnCompletionListener, OnErrorListener, OnBufferingUpdateListener {
    /**
     * 状态-未初始化
     */
    public static final int STATE_UNINITIALIZED = 0;
    /**
     * 状态-初始化完毕
     */
    public static final int STATE_INITIALIZED = 1;
    /**
     * 状态-准备完毕
     */
    public static final int STATE_PREPARED = 2;
    /**
     * 状态-播放
     */
    public static final int STATE_PLAYING = 3;
    /**
     * 状态-暂停
     */
    public static final int STATE_PAUSE = 4;
    /**
     * 状态-停止
     */
    public static final int STATE_STOP = 5;

    public int mState;
    private int mSampleTime;

    private static Mp3Player mInstance;
    public MediaPlayer mMediaPlayer;
    private AudioPlayerListener mAudioPlayerListener;

    private final Handler mHandler;
    private Runnable mUndatePlayPositionRunnable = new Runnable() {

        @Override
        public void run() {
            undatePosition();
        }
    };
    public String path;

    public void setmAudioPlayerListener(AudioPlayerListener mAudioPlayerListener) {
        this.mAudioPlayerListener = mAudioPlayerListener;
    }

    private Mp3Player() {
        super();
        mState = STATE_UNINITIALIZED;
        mSampleTime = 100;

        if (Looper.myLooper() == null) {
            Looper.prepare();
            mHandler = new Handler();
            Looper.loop();
        } else {
            mHandler = new Handler();
        }
    }

    public static Mp3Player getInstance() {
        mInstance = new Mp3Player();
        return mInstance;
    }

    /**
     * 初始化MediaPlayer
     */
    public void init(AudioPlayerListener listener) {
        if (mState == STATE_UNINITIALIZED) {
            if (listener == null) {
                throw new RuntimeException("AudioPlayerListener not null");
            }
            if (mMediaPlayer == null) {
                mMediaPlayer = new MediaPlayer();
            }
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnBufferingUpdateListener(this);
            mMediaPlayer.setOnErrorListener(this);
            mState = STATE_INITIALIZED;
        }
        this.mAudioPlayerListener = listener;
    }


    public boolean isPlaying(){
        if (mMediaPlayer!=null){
            return mMediaPlayer.isPlaying();
        }else {
            return false;
        }
    }

    /**
     * 设置音频源(异步)
     *
     * @param path
     * @return 返回Duration
     * @throws IllegalArgumentException
     * @throws SecurityException
     * @throws IllegalStateException
     * @throws IOException
     */
    public boolean setDataSource(String path) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
        this.path = path;
        if (mState == STATE_UNINITIALIZED) {
            throw new RuntimeException("设置音频源之前请进行初始化");
        }
        if (mState == STATE_PAUSE || mState == STATE_PLAYING) {
            stop();
        }
        if (TextUtils.isEmpty(path)) {
            return false;
        }
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(path);
        mMediaPlayer.prepareAsync();
        return true;
    }


    public String getPath() {
        return path;
    }

    /**
     * 设置音频源(异步)
     *
     * @param fileDescriptor
     * @return 返回Duration
     * @throws IllegalArgumentException
     * @throws SecurityException
     * @throws IllegalStateException
     * @throws IOException
     */
    public boolean setDataSource(AssetFileDescriptor fileDescriptor) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {

        if (mState == STATE_UNINITIALIZED) {
            throw new RuntimeException("设置音频源之前请进行初始化");
        }
        if (mState == STATE_PAUSE || mState == STATE_PLAYING) {
            stop();
        }
        if (fileDescriptor == null) {
            return false;
        }
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(),
                fileDescriptor.getLength());
        mMediaPlayer.prepareAsync();
        return true;
    }

    public void play() throws IllegalStateException, IOException {
        if (mState == STATE_UNINITIALIZED) {
            throw new RuntimeException("播放前请进行初始化");
        }
        if (mState == STATE_INITIALIZED) {
            throw new RuntimeException("播放前请设置音频源");
        }
        if (!mMediaPlayer.isPlaying()) {
            mMediaPlayer.start();
            mState = STATE_PLAYING;
            undatePosition();
        }
    }

    public void pause() {
        try {
            if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
                mState = STATE_PAUSE;
            }
        } catch (Exception e) {

        }
    }

    public void stop() {
        if (mMediaPlayer != null && (mMediaPlayer.isPlaying() || mState == STATE_PAUSE)) {
            mMediaPlayer.stop();
            mState = STATE_STOP;
        }
    }

    public void release() {
        mState = STATE_UNINITIALIZED;
        stop();
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
        }
        mMediaPlayer = null;
        mAudioPlayerListener = null;
        mInstance = null;
    }

    public void seekTo(int msec) {
        if (mMediaPlayer != null) {
            mMediaPlayer.seekTo(msec);
        }
    }

    /**
     * 获取当前播放位置
     *
     * @return
     */
    public int getCurrentPostion() {
        return mMediaPlayer == null ? -1 : mMediaPlayer.getCurrentPosition();
    }

    /**
     * 获取总时间
     *
     * @return
     */
    public int getDuration() {

        return mMediaPlayer == null ? -1 : mMediaPlayer.getDuration();
    }

    /**
     * 获取当前状态
     *
     * @return
     */
    public int getState() {
        return mState;
    }

    /**
     * 更新进度
     */
    private void undatePosition() {
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
            mAudioPlayerListener.onUpdateCurrentPosition(mMediaPlayer.getCurrentPosition());
            mHandler.postDelayed(mUndatePlayPositionRunnable, mSampleTime);
        }
    }

    @Override
    public void onBufferingUpdate(MediaPlayer mp, int percent) {
        mAudioPlayerListener.onBufferingUpdate(mp, percent);
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mState = STATE_PREPARED;
        mAudioPlayerListener.onPrepared();
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        mState = STATE_STOP;
        mAudioPlayerListener.onCompletion();
    }

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

    public void reset() {
        if (mMediaPlayer!=null){
            mMediaPlayer.reset();
        }
    }

    public interface AudioPlayerListener {
        /**
         * AudioPlayer准备完成时回调
         */
        void onPrepared();

        /**
         * AudioPlayer播放完成时回调
         */
        void onCompletion();

        /**
         * AudioPlayer播放期间每个设置的取样时间间隔回调一次
         *
         * @param position 当前播放位置
         */
        void onUpdateCurrentPosition(int position);

        /**
         * 缓存进度回调
         *
         * @param mp
         * @param percent
         */
        void onBufferingUpdate(MediaPlayer mp, int percent);

        /**
         * Called to indicate an error.
         *
         * @param mp
         * @param what
         * @param extra
         */
        void onError(MediaPlayer mp, int what, int extra);

    }
}


Copy the code

Welcome to “Android Evolution”