Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器

Android 中封装优雅的 MediaPlayer 音频播放器,支持多个播放器实例的示例:

public class AudioPlayer implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {

    private static AudioPlayer instance;
    private List<AudioPlayerListener> listeners = new ArrayList<>();
    private MediaPlayer mediaPlayer;
    private Context context;

    private AudioPlayer(Context context) {
        this.context = context;
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setOnCompletionListener(this);
    }

    public static synchronized AudioPlayer getInstance(Context context) {
        if (instance == null) {
            instance = new AudioPlayer(context);
        }
        return instance;
    }

    public void addListener(AudioPlayerListener listener) {
        listeners.add(listener);
    }

    public void removeListener(AudioPlayerListener listener) {
        listeners.remove(listener);
    }

    public void play(String url) {
        try {
            mediaPlayer.reset();
            mediaPlayer.setDataSource(url);
            mediaPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
        }
    }

    public void release() {
        mediaPlayer.release();
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
        for (AudioPlayerListener listener : listeners) {
            listener.onAudioPlayerStart();
        }
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        for (AudioPlayerListener listener : listeners) {
            listener.onAudioPlayerStop();
        }
    }

    public interface AudioPlayerListener {
        void onAudioPlayerStart();
        void onAudioPlayerStop();
    }
}

上述代码中,使用 getInstance() 方法获取 AudioPlayer 的单例对象,参数传入 Context 对象。

getInstance() 方法中判断单例对象是否为空,如果为空则创建新的 AudioPlayer 对象,否则返回已有的单例对象。

这样可以保证在同一进程中,只有一个 AudioPlayer 实例,方便管理多个音频文件的播放。

该类有一个 AudioPlayerListener 接口,用于监听播放器的状态。在播放器的 onPrepared()onCompletion() 方法中触发回调,通知所有监听器。

可以通过调用 addListener()removeListener() 方法添加或移除监听器。

调用 play() 方法以播放音频,传入音频的 URL 地址,调用 stop() 方法以停止播放,调用 release() 方法以释放播放器资源。

可以按照以下步骤来调用单例模式的 AudioPlayer 类:

  1. 在需要使用 AudioPlayer 的地方,先获取 Context 对象,例如在 MainActivity 中:
public class MainActivity extends AppCompatActivity {

    private AudioPlayer audioPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        audioPlayer = AudioPlayer.getInstance(this);
    }

    //...
}

2.添加一个 AudioPlayerListener 接口的实现,监听音频播放开始和结束事件:

public class MainActivity extends AppCompatActivity implements AudioPlayer.AudioPlayerListener {

    // ...

    @Override
    public void onAudioPlayerStart() {
        // 音频播放开始
    }

    @Override
    public void onAudioPlayerStop() {
        // 音频播放结束
    }
}

3.在需要播放音频的地方,调用 AudioPlayerplay() 方法:

audioPlayer.play(url);

其中,url 是音频文件的 URL 地址。

4.在需要停止音频播放的地方,调用 AudioPlayerstop() 方法:

 
 
audioPlayer.stop();

如果需要释放 MediaPlayer 实例,可以调用 AudioPlayerrelease() 方法:

audioPlayer.release();

5.通过 addListener()removeListener() 方法,添加和删除 AudioPlayerListener 的实现,监听音频播放事件:

audioPlayer.addListener(this); 
audioPlayer.removeListener(this);

这样就可以使用单例模式的 AudioPlayer 类播放音频了。需要注意的是,由于单例实例只有一个,调用 release() 方法时要谨慎,避免对其他地方的音频播放产生影响。

为了避免对其他地方的音频播放产生影响,可以在 AudioPlayer 类中添加一些控制音频焦点的方法。

这里需要用到 AudioManager 类,该类提供了访问音频系统服务的方法,可以控制应用程序的音频会话和音频路由。

具体实现如下:

public class AudioPlayer implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener {

    private static AudioPlayer instance;
    private List<AudioPlayerListener> listeners = new ArrayList<>();
    private MediaPlayer mediaPlayer;
    private Context context;
    private AudioManager audioManager;
    private boolean audioFocusGranted = false;

    private AudioPlayer(Context context) {
        this.context = context;
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setOnCompletionListener(this);

        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

    public static synchronized AudioPlayer getInstance(Context context) {
        if (instance == null) {
            instance = new AudioPlayer(context);
        }
        return instance;
    }

    public void addListener(AudioPlayerListener listener) {
        listeners.add(listener);
    }

    public void removeListener(AudioPlayerListener listener) {
        listeners.remove(listener);
    }

    public void play(String url) {
        try {
            mediaPlayer.reset();
            mediaPlayer.setDataSource(url);
            mediaPlayer.prepareAsync();
            requestAudioFocus();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
        }
        abandonAudioFocus();
    }

    public void release() {
        stop();
        mediaPlayer.release();
    }

    private void requestAudioFocus() {
        if (!audioFocusGranted) {
            int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
            audioFocusGranted = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
        }
    }

    private void abandonAudioFocus() {
        if (audioFocusGranted) {
            audioManager.abandonAudioFocus(this);
            audioFocusGranted = false;
        }
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
        for (AudioPlayerListener listener : listeners) {
            listener.onAudioPlayerStart();
        }
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        for (AudioPlayerListener listener : listeners) {
            listener.onAudioPlayerStop();
        }
        abandonAudioFocus();
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                mediaPlayer.start();
                break;
            case AudioManager.AUDIOFOCUS_LOSS:
                stop();
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                mediaPlayer.pause();
                break;
        }
    }

    public interface AudioPlayerListener {
        void onAudioPlayerStart();
        void onAudioPlayerStop();
    }
}

上述代码中:

  1. audioManager 对象通过 getSystemService() 方法获取 AUDIO_SERVICE 服务来初始化;
  2. play() 方法在播放音频之前请求音频焦点;
  3. stop() 方法在停止播放音频之后释放音频焦点;
  4. requestAudioFocus() 方法用于请求音频焦点,当焦点可用时,音频焦点授权标志 audioFocusGranted 会被设置为 true;
  5. abandonAudioFocus() 方法用于释放音频焦点,当焦点被释放后,音频焦点授权标志 audioFocusGranted 会被设置为 false;
  6. onAudioFocusChange() 方法在焦点发生变化时被调用,根据不同的焦点变化类型采取不同的处理方式;
  7. onCompletion() 方法中,当音频播放完成后,释放音频焦点。

这种实现方式可以满足大多数情况,但仍可能存在一些问题,例如:

  1. 当多个音频焦点请求同时发生时,可能会发生竞争问题;
  2. 不同设备上的音频焦点分配方式可能不同,可能需要进行额外的适配。

因此,实现完美控制音频焦点的方式需要根据具体情况进行调整和完善。

在多个音频焦点请求同时发生时,可能会发生竞争问题,最终的效果可能不如预期。为了优化竞争问题,可以考虑以下两种方案:

  1. 策略模式:通过策略模式,我们可以将不同音频焦点请求的处理方式分开,对不同的请求进行不同的处理。这样,可以避免彼此之间产生竞争问题。同时,策略模式可以方便地扩展新的音频焦点策略。

  2. 优先级系统:通过优先级系统,我们可以为不同的音频焦点请求设置不同的优先级,当多个请求同时到达时,根据优先级依次处理,避免竞争或者其他问题的发生。

同时,还有一些额外的技巧可以使用:

  1. 队列处理:当多个音频焦点请求同时到达时,可以通过队列处理,先进先出的方式,避免竞争或冲突的发生。

  2. 回调处理:当多个音频焦点请求同时到达时,可以通过回调处理,当某个请求处理完成后再处理下一个请求,避免发生竞争或冲突的发生。

综上所述,无论采用策略模式还是优先级系统,队列处理还是回调处理,处理多个音频焦点请求时应选一具有优化性能和处理方式清晰度的方法。

以下是一个简单的示例代码,使用优先级系统来处理多个音频焦点请求:

public class AudioFocusManager {
    
    private final List<AudioFocusRequest> requests = new ArrayList<>(); // 待处理的音频焦点请求列表
    
    public void requestAudioFocus(AudioFocusRequest request) {
        requests.add(request); // 将请求加入待处理列表
        Collections.sort(requests, new AudioFocusRequestComparator()); // 按照优先级排序
        processRequests(); // 处理请求
    }
    
    private void processRequests() {
        AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        for (AudioFocusRequest request : requests) {
            int result = audioManager.requestAudioFocus(request.getOnAudioFocusChangeListener(),
                    request.getStreamType(), request.getDurationHint());
            if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                // 请求被拒绝
                break;
            } else {
                // 请求被接受,处理下一个请求
                requests.remove(request);
            }
        }
    }
    
    private static class AudioFocusRequestComparator implements Comparator<AudioFocusRequest> {
        @Override
        public int compare(AudioFocusRequest r1, AudioFocusRequest r2) {
            return r2.getPriority() - r1.getPriority();
        }
    }
}

在上述代码中,我们定义了一个AudioFocusManager类来处理多个音频焦点请求。当我们调用requestAudioFocus方法时,将请求加入待处理列表,然后按照优先级排序,依次处理请求。如果请求被拒绝,则停止处理,否则继续处理下一个请求。

值得注意的是,此处假设AudioFocusRequest已经实现。具体实现方式可以根据实际业务需求来定制。例如,可以使用策略模式来实现不同音频焦点请求的处理方式,或者使用回调来更新 UI 界面等。

要使用一个类似于上述示例中的音频焦点管理器,以下是一些步骤:

  1. 创建一个名为AudioFocusRequest的类,该类应该具有以下属性和方法:
  • priority - 请求的优先级,以整数形式表示
  • durationHint - 请求的持续时间,以毫秒为单位
  • streamType - 请求的音频类型(例如,STREAM_MUSIC
  • onAudioFocusChangeListener - 处理焦点变化的监听器。您可以选择使用内部类来实现它。
  1. 创建一个名为AudioFocusManager的类,在该类中,编写一个requestAudioFocus方法,该方法接受一个AudioFocusRequest对象,将其添加到一个列表中,并使用优先级排序算法对待处理的请求进行排序。然后,进行请求处理并跟踪列表中未处理的其他请求。

  2. 在需要管理音频焦点的 Activity 中,实例化一个AudioFocusManager对象,并在需要获取音频焦点的地方调用requestAudioFocus方法,传入一个已经实例化好的AudioFocusRequest对象即可。

这样,您的应用程序将能够有效地控制音频焦点,并处理多个请求。

以下是一个简单的示例,演示如何使用上述步骤来管理音频焦点。

首先,创建名为AudioFocusRequest的 Java 类,该类应该具有以下属性和方法:

public class AudioFocusRequest {
    private int priority;
    private int durationHint;
    private int streamType;
    private OnAudioFocusChangeListener onAudioFocusChangeListener;

    public AudioFocusRequest(int priority, int durationHint, int streamType, OnAudioFocusChangeListener onAudioFocusChangeListener) {
        this.priority = priority;
        this.durationHint = durationHint;
        this.streamType = streamType;
        this.onAudioFocusChangeListener = onAudioFocusChangeListener;
    }

    public int getPriority() {
        return priority;
    }

    public int getDurationHint() {
        return durationHint;
    }

    public int getStreamType() {
        return streamType;
    }

    public OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
        return onAudioFocusChangeListener;
    }
}

然后,创建名为AudioFocusManager的 Java 类,并编写一个requestAudioFocus方法,代码如下所示:

public class AudioFocusManager {
    private List<AudioFocusRequest> audioFocusRequests = new ArrayList<>();

    public void requestAudioFocus(AudioFocusRequest audioFocusRequest) {
        audioFocusRequests.add(audioFocusRequest);
        Collections.sort(audioFocusRequests, new Comparator<AudioFocusRequest>() {
            @Override
            public int compare(AudioFocusRequest r1, AudioFocusRequest r2) {
                return r2.getPriority() - r1.getPriority();
            }
        });

        processAudioFocusRequests();
    }

    private void processAudioFocusRequests() {
        for (int i = 0; i < audioFocusRequests.size(); i++) {
            AudioFocusRequest audioFocusRequest = audioFocusRequests.get(i);

            int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;

            AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            if (audioManager != null) {
                result = audioManager.requestAudioFocus(audioFocusRequest.getOnAudioFocusChangeListener(),
                        audioFocusRequest.getStreamType(), audioFocusRequest.getDurationHint());
            }

            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                audioFocusRequests.remove(audioFocusRequest);
            }
        }
    }
}

最后,您可以在需要管理音频焦点的 Activity 中,按照示例代码中的方式来使用AudioFocusManagerAudioFocusRequest,例如:

AudioFocusRequest audioFocusRequest = new AudioFocusRequest(AudioManager.AUDIOFOCUS_GAIN, 1000, AudioManager.STREAM_MUSIC, new OnAudioFocusChangeListener() {
    @Override
    public void onAudioFocusChange(int focusChange) {
        // 处理音频焦点变化
    }
});

AudioFocusManager audioFocusManager = new AudioFocusManager();
audioFocusManager.requestAudioFocus(audioFocusRequest);

在此示例中,我们创建了一个AudioFocusRequest对象, 使用AudioFocusManager请求音频焦点,并指定处理请求的回调方法。

以下是一个使用AudioFocusManager类来处理多个音频焦点请求的示例代码:

public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {

    private AudioManager audioManager;
    private Map<AudioFocusRequest, AudioFocusChangeListener> audioFocusRequests;

    public AudioFocusManager(Context context) {
        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        audioFocusRequests = new HashMap<>();
    }

    public void requestAudioFocus(AudioFocusRequest audioFocusRequest, AudioFocusChangeListener audioFocusChangeListener) {
        audioFocusRequests.put(audioFocusRequest, audioFocusChangeListener);
        updateAudioFocus();
    }

    public void abandonAudioFocus(AudioFocusRequest audioFocusRequest) {
        audioFocusRequests.remove(audioFocusRequest);
        updateAudioFocus();
    }

    private void updateAudioFocus() {
        AudioFocusRequest topAudioFocusRequest = getTopAudioFocusRequest();
        if (topAudioFocusRequest == null) {
            audioManager.abandonAudioFocus(this);
        } else {
            int focusRequestResult = audioManager.requestAudioFocus(
                    topAudioFocusRequest.getAudioFocusRequest(),
                    AudioManager.STREAM_MUSIC,
                    AudioManager.AUDIOFOCUS_GAIN);
            if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                AudioFocusChangeListener audioFocusChangeListener = audioFocusRequests.get(topAudioFocusRequest);
                if (audioFocusChangeListener != null) {
                    audioFocusChangeListener.onAudioFocusChanged(true);
                }
            }
        }
    }

    private AudioFocusRequest getTopAudioFocusRequest() {
        AudioFocusRequest topAudioFocusRequest = null;
        for (AudioFocusRequest audioFocusRequest : audioFocusRequests.keySet()) {
            if (topAudioFocusRequest == null || audioFocusRequest.getPriority() > topAudioFocusRequest.getPriority()) {
                topAudioFocusRequest = audioFocusRequest;
            }
        }
        return topAudioFocusRequest;
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        AudioFocusRequest topAudioFocusRequest = getTopAudioFocusRequest();
        if (topAudioFocusRequest != null) {
            AudioFocusChangeListener audioFocusChangeListener = audioFocusRequests.get(topAudioFocusRequest);
            if (audioFocusChangeListener != null) {
                switch (focusChange) {
                    case AudioManager.AUDIOFOCUS_GAIN:
                        audioFocusChangeListener.onAudioFocusChanged(true);
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS:
                        audioFocusRequests.clear();
                        audioFocusChangeListener.onAudioFocusChanged(false);
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        audioFocusChangeListener.onAudioFocusChanged(false);
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        audioFocusChangeListener.onAudioFocusChanged(false);
                        break;
                }
            }
        }
    }

    public interface AudioFocusChangeListener {
        void onAudioFocusChanged(boolean hasAudioFocus);
    }

    public static class AudioFocusRequest {
        private final int priority;
        private final AudioAttributes audioAttributes;

        public AudioFocusRequest(int priority, AudioAttributes audioAttributes) {
            this.priority = priority;
            this.audioAttributes = audioAttributes;
        }

        public int getPriority() {
            return priority;
        }

        public AudioAttributes getAudioAttributes() {
            return audioAttributes;
        }

        public androidx.media.AudioAttributesCompat getAudioAttributesCompat() {
            return androidx.media.AudioAttributesCompat.wrap(audioAttributes);
        }

        public AudioFocusRequestCompat getAudioFocusRequestCompat() {
            return new AudioFocusRequestCompat.Builder(getPriority())
                    .setAudioAttributes(getAudioAttributesCompat())
                    .build();
        }

        public AudioFocusRequest getAudioFocusRequest() {
            return new AudioFocusRequest.Builder(getPriority())
                    .setAudioAttributes(audioAttributes)
                    .build();
        }
    }
}

这个示例代码中的AudioFocusManager类包含以下方法:

  • requestAudioFocus(AudioFocusRequest audioFocusRequest, AudioFocusChangeListener audioFocusChangeListener):向系统请求音频焦点,并提供了一个回调函数来通知应用程序当音频焦点状态改变时。多次调用此方法将根据请求的优先级来处理多个音频焦点请求。
  • abandonAudioFocus(AudioFocusRequest audioFocusRequest):放弃音频焦点,并在内部更新所有请求焦点的对象列表。
  • updateAudioFocus():根据当前优先级的顺序使用AudioManager请求音频焦点,在焦点状态改变时通知相应的监听者。
  • getTopAudioFocusRequest():返回一个包含最高优先级的AudioFocusRequest对象。
  • `onAudioFocus

以下是一个使用AudioFocusManager类来处理多个音频焦点请求的完整示例代码:

public class MainActivity extends AppCompatActivity {

    private AudioFocusManager audioFocusManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        audioFocusManager = new AudioFocusManager(this);
    }

    @Override
    protected void onStart() {
        super.onStart();

        AudioFocusManager.AudioFocusRequest audioFocusRequest1 = new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build());

        AudioFocusManager.AudioFocusRequest audioFocusRequest2 = new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build());

        audioFocusManager.requestAudioFocus(audioFocusRequest1, new AudioFocusManager.AudioFocusChangeListener() {
            @Override
            public void onAudioFocusChanged(boolean hasAudioFocus) {
                if (hasAudioFocus) {
                    // 开始播放音乐
                } else {
                    // 暂停或停止音乐播放
                }
            }
        });

        audioFocusManager.requestAudioFocus(audioFocusRequest2, new AudioFocusManager.AudioFocusChangeListener() {
            @Override
            public void onAudioFocusChanged(boolean hasAudioFocus) {
                if (hasAudioFocus) {
                    // 播放通知提示音
                } else {
                    // 停止播放通知提示音
                }
            }
        });
    }

    @Override
    protected void onStop() {
        super.onStop();

        audioFocusManager.abandonAudioFocus(new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build()));

        audioFocusManager.abandonAudioFocus(new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build()));
    }
}

在这个示例中,我们在MainActivityonStart()方法中请求了两种不同类型的音频焦点:AudioFocusRequest对象audioFocusRequest1audioFocusRequest2,它们分别代表媒体播放和通知提示音。我们还为每个焦点请求设置了对应的AudioFocusChangeListener来处理焦点状态的变化。

MainActivityonStop()方法中我们请求释放这两种音频焦点。

如果需要同时管理多个不同的播放器(如同时播放背景音乐、音效和语音提示等),可以通过实例化多个AudioFocusManager类来实现,每个类只负责管理一个播放器。需要注意的是,多个AudioFocusManager实例之间的焦点请求和变化都是独立的,因此需要在适当的时候根据自己的业务逻辑进行相应的协调。

以下是一个同时管理三个播放器的简单示例代码:

public class MainActivity extends AppCompatActivity {

    private AudioFocusManager mediaPlayer1FocusManager;
    private AudioFocusManager mediaPlayer2FocusManager;
    private AudioFocusManager mediaPlayer3FocusManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mediaPlayer1FocusManager = new AudioFocusManager(this);
        mediaPlayer2FocusManager = new AudioFocusManager(this);
        mediaPlayer3FocusManager = new AudioFocusManager(this);
    }

    @Override
    protected void onStart() {
        super.onStart();

        AudioFocusManager.AudioFocusRequest audioFocusRequest1 = new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build());

        AudioFocusManager.AudioFocusRequest audioFocusRequest2 = new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_GAME)
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .build());

        AudioFocusManager.AudioFocusRequest audioFocusRequest3 = new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build());

        mediaPlayer1FocusManager.requestAudioFocus(audioFocusRequest1, new AudioFocusManager.AudioFocusChangeListener() {
            @Override
            public void onAudioFocusChanged(boolean hasAudioFocus) {
                if (hasAudioFocus) {
                    // 开始播放媒体音乐
                } else {
                    // 暂停或停止媒体音乐播放
                }
            }
        });

        mediaPlayer2FocusManager.requestAudioFocus(audioFocusRequest2, new AudioFocusManager.AudioFocusChangeListener() {
            @Override
            public void onAudioFocusChanged(boolean hasAudioFocus) {
                if (hasAudioFocus) {
                    // 开始播放游戏音效
                } else {
                    // 停止播放游戏音效
                }
            }
        });

        mediaPlayer3FocusManager.requestAudioFocus(audioFocusRequest3, new AudioFocusManager.AudioFocusChangeListener() {
            @Override
            public void onAudioFocusChanged(boolean hasAudioFocus) {
                if (hasAudioFocus) {
                    // 开始播放语音提示
                } else {
                    // 停止播放语音提示
                }
            }
        });
    }

    @Override
    protected void onStop() {
        super.onStop();

        mediaPlayer1FocusManager.abandonAudioFocus(new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build()));

        mediaPlayer2FocusManager.abandonAudioFocus(new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_GAME)
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .build()));

        mediaPlayer3FocusManager.abandonAudioFocus(new AudioFocusManager.AudioFocusRequest(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build()));
    }
}

在这个示例中,我们实例化了三个AudioFocusManager对象来依次处理三个不同的播放器。每个播放器都有自己的焦点请求和对应的焦点状态监听器。在onStart()方法中,我们通过这三个焦点管理器分别请求了不同的音频焦点。在onStop()方法中,我们则请求释放三种不同的音

如果你想在前面示例中使用已经封装好的AudioPlayer来播放音频文件,可以按照以下步骤进行修改:

  1. 创建一个AudioPlayer对象。

AudioPlayer audioPlayer = new AudioPlayer();

  1. 在获取音频焦点后,使用AudioPlayer对象播放音频文件,示例代码如下:
  2. mediaPlayer1FocusManager.requestAudioFocus(audioFocusRequest1, new AudioFocusManager.AudioFocusChangeListener() {
        @Override
        public void onAudioFocusChanged(boolean hasAudioFocus) {
            if (hasAudioFocus) {
                // 开始播放媒体音乐
                audioPlayer.play(MainActivity.this, R.raw.background_music);
            } else {
                // 暂停或停止媒体音乐播放
                audioPlayer.stop();
            }
        }
    });
    

  3. 在对应的停止音频播放的位置,调用AudioPlayer对象的stop()方法停止当前播放的音频文件:
  4.  

    audioPlayer.stop();

    如果需要播放多个音频文件,可以根据需要重复上述步骤,每次创建一个新的AudioPlayer对象并调用相应的方法来实现。

  5. 下面是结合 AudioFocusManager 的完整示例代码:

public class AudioPlayer implements AudioFocusManager.AudioFocusListener {
    private static AudioPlayer instance;
    private HashMap<Integer, MediaPlayer> mediaPlayerMap;
    private AudioFocusManager audioFocusManager;

    private AudioPlayer(Context context) {
        mediaPlayerMap = new HashMap<>();
        audioFocusManager = new AudioFocusManager(context, this);
    }

    public static synchronized AudioPlayer getInstance(Context context) {
        if (instance == null) {
            instance = new AudioPlayer(context);
        }
        return instance;
    }

    public void play(Context context, int resId, boolean looping) {
        if (mediaPlayerMap.containsKey(resId)) {
            MediaPlayer mediaPlayer = mediaPlayerMap.get(resId);
            mediaPlayer.setLooping(looping);
            if (!mediaPlayer.isPlaying()) {
                mediaPlayer.start();
            }
        } else {
            MediaPlayer mediaPlayer = MediaPlayer.create(context, resId);
            mediaPlayerMap.put(resId, mediaPlayer);
            mediaPlayer.setLooping(looping);
            mediaPlayer.start();
        }
        audioFocusManager.requestAudioFocus();
    }

    public void pause(int resId) {
        if (mediaPlayerMap.containsKey(resId)) {
            MediaPlayer mediaPlayer = mediaPlayerMap.get(resId);
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.pause();
            }
        }
    }

    public void stop(int resId) {
        if (mediaPlayerMap.containsKey(resId)) {
            MediaPlayer mediaPlayer = mediaPlayerMap.get(resId);
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            mediaPlayer.release();
            mediaPlayerMap.remove(resId);
        }
        audioFocusManager.abandonAudioFocus();
    }

    public void stopAll() {
        for (MediaPlayer mediaPlayer : mediaPlayerMap.values()) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            mediaPlayer.release();
        }
        mediaPlayerMap.clear();
        audioFocusManager.abandonAudioFocus();
    }

    @Override
    public void onAudioFocusGained() {
        // do nothing
    }

    @Override
    public void onAudioFocusLost() {
        stopAll();
    }

    @Override
    public void onAudioFocusLostTransient() {
        stopAll();
    }

    @Override
    public void onAudioFocusLostTransientCanDuck() {
        // do nothing
    }
}

在上面的代码中,我们将 AudioPlayer 实现为单例模式,然后我们将 AudioFocusManager 添加到 AudioPlayer 类中,以控制音频焦点。

play() 方法中,我们先根据 looping 参数在 Map 中查询是否已存在所需音频的 MediaPlayer 对象。如果存在,则设置循环播放属性并开始播放;否则,我们创建一个新的 MediaPlayer 对象并将其添加到 Map 中。接着,我们使用 requestAudioFocus() 方法请求音频焦点以确保我们可以正常播放该音频。

stop()stopAll() 方法中,我们先停止所有的 MediaPlayer 对象并释放它们占用的资源。接着,我们使用 abandonAudioFocus() 方法归还我们之前请求的音频焦点。

最后,在 AudioFocusListener 的回调方法中,我们根据不同的焦点失去情况来停止播放。

这就是结合 AudioFocusManagerAudioPlayer 完整示例代码。希望能够帮助你实现你的音频播放器。

下面是使用 AudioPlayer 类播放音频的示例代码:

public class MainActivity extends AppCompatActivity {
    private AudioPlayer audioPlayer;
    private int audioResId = R.raw.audio_file;
    private boolean isLooping = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        audioPlayer = AudioPlayer.getInstance(this);
    }

    public void onClickPlay(View view) {
        audioPlayer.play(this, audioResId, isLooping);
    }

    public void onClickPause(View view) {
        audioPlayer.pause(audioResId);
    }

    public void onClickStop(View view) {
        audioPlayer.stop(audioResId);
    }

    public void onClickStopAll(View view) {
        audioPlayer.stopAll();
    }
}

在上面的代码中,我们首先在 onCreate() 方法中获取 AudioPlayer 实例。接着,在处理用户交互时,例如点击各种按钮时,我们调用 AudioPlayer 类中相应的方法,以播放、暂停或停止音频。

当用户点击 "Play" 按钮时,我们使用 play() 方法请求音频焦点并播放音频。当用户点击 "Pause" 按钮时,我们使用 pause() 方法暂停音频的播放。当用户点击 "Stop" 按钮时,我们使用 stop() 方法停止音频的播放并释放占用的资源。当用户点击 "Stop All" 按钮时,我们使用 stopAll() 方法来停止播放所有音频并同时释放占用的资源。

这就是使用 AudioPlayer 类播放音频的示例代码。希望它能帮助你实现你的音频播放器应用程序。

猜你喜欢

转载自blog.csdn.net/ck3345143/article/details/129944532