在 Android Studio 的服务中是否需要 运行 MediaPlayer?

Do I need to run MediaPlayer in a service in Android Studio?

以下代码来自project.

可能是我用MediaRecorder控件的时候操作时间比较长,所以作者运行 MediaRecorder在一个服务中,可以看到代码B.

可能播放一段音频也是很长时间的操作,所以我觉得作者应该运行 MediaPlayer在一个服务中,但是代码A为什么不这样做呢?

代码A

public final class MediaPlayerHolder implements PlayerAdapter {

    public static final int PLAYBACK_POSITION_REFRESH_INTERVAL_MS = 1000;

    private MediaPlayer mMediaPlayer;
    
    @Override
    public void play() {
        if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
            mMediaPlayer.start();
            if (mPlaybackInfoListener != null) {
                mPlaybackInfoListener.onStateChanged(PlaybackInfoListener.State.PLAYING);
            }
            startUpdatingCallbackWithPosition();
        }
    }
    ...
}

代码B

public class RecordingService extends Service {
    
    public class LocalBinder extends Binder {
        public RecordingService getService() {
            return RecordingService.this;
        }
    }

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

 
    public void startRecording(int duration) {
        setFileNameAndPath();
        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mRecorder.setOutputFile(mFilePath);

        mRecorder.setMaxDuration(duration); // set the max duration, after which the Service is stopped
        mRecorder.setAudioChannels(1);
        mRecorder.setAudioSamplingRate(44100);
        mRecorder.setAudioEncodingBitRate(192000);

       ...
    }

}

使用Service的目的是让你的代码运行在后台,你可以进行不需要用户界面的操作,甚至还有你的代码运行 ] 超出 activity 的 onDestoy() 方法。这就是音乐播放器允许您在关闭应用程序 Activity.

后仍能听音乐的方式

有 3 种服务类型:

Foreground Service

Background Service

Bound Service

为什么服务中有代码 B 而代码 A 没有?

来自绑定服务概述:

A bound service is the server in a client-server interface. It allows components (such as activities) to bind to the service, send requests, receive responses, and perform interprocess communication (IPC). A bound service typically lives only while it serves another application component and does not run in the background indefinitely.

换句话说,它允许与其他应用程序或跨不同进程进行通信。这就是作者使用该服务的主要原因。与性能无关。

关于性能:

代码 B 不考虑性能。

来自 Service 概述:

Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. You should run any blocking operations on a separate thread within the service to avoid Application Not Responding (ANR) errors.

所以仅仅使用服务并不能保证性能。在代码 B 中,我们有方法 startRecording(),它初始化 MediaRecorder 并为 Recording 设置一些参数。这并不意味着此方法将在服务启动后立即 运行 。作者使用了Bound Service,可以通过以下方式判断:

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

这意味着绑定到它的任何组件(例如 activity 或其他进程都可以调用其方法 startRecording() )。请查看绑定服务 link 了解更多信息。在项目中,这是从 RecordViewModel.startRecording().

调用的

如果您担心性能。代码 B 应该在 startRecording(int duration) 内启动一个新线程。有很多方法可以做到这一点。这是一个:

public void startRecording(int duration) {

( new Thread( new Runnable() {

        @Override
        public void run() {
                

            setFileNameAndPath();
            mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mRecorder.setOutputFile(mFilePath);

            mRecorder.setMaxDuration(duration); // set the max duration, after which the Service is stopped
            mRecorder.setAudioChannels(1);
            mRecorder.setAudioSamplingRate(44100);
            mRecorder.setAudioEncodingBitRate(192000);

       ...



        }

    }) ).start();


}

至于代码 A,唯一发生的事情是调用 MediaPlayer.start(),它已经在内部启动了一个新线程。

来自 MediaPlayer class 源代码:

public void start() throws IllegalStateException {
        //FIXME use lambda to pass startImpl to superclass
        final int delay = getStartDelayMs();
        if (delay == 0) {
            startImpl();
        } else {
            new Thread() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    baseSetStartDelayMs(0);
                    try {
                        startImpl();
                    } catch (IllegalStateException e) {
                        // fail silently for a state exception when it is happening after
                        // a delayed start, as the player state could have changed between the
                        // call to start() and the execution of startImpl()
                    }
                }
            }.start();
        }
    }

如果代码 A 关注性能,那么 MediaPlayerHolder.loadMedia(字符串路径)应该使用单独的线程。

所以回答你的问题。不,您不需要 运行 服务中的 MediaPlayer。这取决于您的要求。

此致