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();}@Overridepublic void onPrepared(MediaPlayer mp) {mp.start();for (AudioPlayerListener listener : listeners) {listener.onAudioPlayerStart();}}@Overridepublic 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;@Overrideprotected 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 {// ...@Overridepublic void onAudioPlayerStart() {// 音频播放开始}@Overridepublic 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;}}@Overridepublic void onPrepared(MediaPlayer mp) {mp.start();for (AudioPlayerListener listener : listeners) {listener.onAudioPlayerStart();}}@Overridepublic void onCompletion(MediaPlayer mp) {for (AudioPlayerListener listener : listeners) {listener.onAudioPlayerStop();}abandonAudioFocus();}@Overridepublic 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> {@Overridepublic 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>() {@Overridepublic 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() {@Overridepublic 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;}@Overridepublic 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);audioFocusManager = new AudioFocusManager(this);}@Overrideprotected 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() {@Overridepublic void onAudioFocusChanged(boolean hasAudioFocus) {if (hasAudioFocus) {// 开始播放音乐} else {// 暂停或停止音乐播放}}});audioFocusManager.requestAudioFocus(audioFocusRequest2, new AudioFocusManager.AudioFocusChangeListener() {@Overridepublic void onAudioFocusChanged(boolean hasAudioFocus) {if (hasAudioFocus) {// 播放通知提示音} else {// 停止播放通知提示音}}});}@Overrideprotected 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mediaPlayer1FocusManager = new AudioFocusManager(this);mediaPlayer2FocusManager = new AudioFocusManager(this);mediaPlayer3FocusManager = new AudioFocusManager(this);}@Overrideprotected 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() {@Overridepublic void onAudioFocusChanged(boolean hasAudioFocus) {if (hasAudioFocus) {// 开始播放媒体音乐} else {// 暂停或停止媒体音乐播放}}});mediaPlayer2FocusManager.requestAudioFocus(audioFocusRequest2, new AudioFocusManager.AudioFocusChangeListener() {@Overridepublic void onAudioFocusChanged(boolean hasAudioFocus) {if (hasAudioFocus) {// 开始播放游戏音效} else {// 停止播放游戏音效}}});mediaPlayer3FocusManager.requestAudioFocus(audioFocusRequest3, new AudioFocusManager.AudioFocusChangeListener() {@Overridepublic void onAudioFocusChanged(boolean hasAudioFocus) {if (hasAudioFocus) {// 开始播放语音提示} else {// 停止播放语音提示}}});}@Overrideprotected 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() {@Overridepublic 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();}@Overridepublic void onAudioFocusGained() {// do nothing}@Overridepublic void onAudioFocusLost() {stopAll();}@Overridepublic void onAudioFocusLostTransient() {stopAll();}@Overridepublic 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;@Overrideprotected 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 类播放音频的示例代码。希望它能帮助你实现你的音频播放器应用程序。

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

  1. Android中如何优雅的定义常量

    本篇博客是笔者的第一篇博客,其实很早之前就有了写博客的想法.但是奈何万事开头难,一直没有下定决心.随着自己积累的知识不断增多,如果不进行总结,不进行思考归纳,有些知识会经常遗忘.对于某些难题,可能当时 ...

  2. 【Android FFMPEG 开发】Android 中使用 FFMPEG 将 PCM 音频采样转为 MP3 格式

    文章目录 一.前置操作 ( 移植 FFMPEG ) 二.FFMPEG 将 PCM 采样转为 MP3 格式的命令 三.Android FFMPEG 混音源代码完整示例 四.博客源码 一.前置操作 ( 移 ...

  3. 【安卓开发 】Android初级开发(九)Android中封装View提供接口供点击事件回调的方法及使用

    自古一楼先上图 package com.example.mydialog;import android.app.Dialog; import android.content.Context; impo ...

  4. Android中使用WebView加载本地html并支持运行JS代码和支持缩放

    场景 Openlayers中使用Image的rotation实现车辆定位导航带转角(判断车辆图片旋转角度): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/art ...

  5. Android视频编辑器(五)音频编解码、从视频中分离音频、音频混音、音频音量调节等

    前言 这篇博客,主要讲解的是android端的音频处理,在开发Android视频编辑器的时候,有一个非常重要的点就是音频的相关处理.比如如何从视频中分离音频(保存为mp3文件),然后分离出来的音频如何 ...

  6. 接口文档示例_在示例中使用Android中的Work Manager

    接口文档示例 在本文中,我们将介绍如何在android中使用工作管理器. 工作管理器是android体系结构组件的一部分,并且可以很好地替代所有先前的调度选项. 其他调度选项,例如JobSchedul ...

  7. iOS音频播放之AudioQueue(一):播放本地音乐

    AudioQueue简介 AudioStreamer说明 AudioQueue详解 AudioQueue工作原理 AudioQueue主要接口 AudioQueueNewOutput AudioQue ...

  8. Android中WebView加载sdcard中的html显示

    场景 Android中使用WebView加载本地html并支持运行JS代码和支持缩放: Android中使用WebView加载本地html并支持运行JS代码和支持缩放_BADAO_LIUMANG_QI ...

  9. Android中使用WebChromeClient显示Openlayers加载本地GeoJson文件显示地图(跨域问题解决)

    场景 Openlayers中加载GeoJson文件显示地图: Openlayers中加载GeoJson文件显示地图_BADAO_LIUMANG_QIZHI的博客-CSDN博客 上面加载显示GeoJso ...

最新文章

  1. 精通ASP.NET MVC ——辅助器方法
  2. docker-compose.yml配置文件详解
  3. python 拆分list,按照对应位置重组
  4. python定位相邻节点_Python selenium 父子、兄弟、相邻节点定位方式详解
  5. access窗体独立运行_由浅入深学习网络设备模拟器HCL 第一篇 安装与运行
  6. Why not inherit from ListT?
  7. 《C Primer Plus》读书笔记
  8. 了解RxJava以及如何在Android应用中使用它
  9. 烂泥: KVM虚拟机Linux系统增加硬盘
  10. Google搜索简单介绍
  11. 捕鱼达人 FishMaster 记录(Done)
  12. java类 英文文献_java英文参考文献
  13. 计算机怎么默认浏览器模式,电脑如何切换浏览器IE内核模式浏览网页
  14. 分享全网免费的CDN网站加速平台(测试)
  15. 男人帮所有手机铃声下载|左永邦手机铃声下载,顾小白手机铃声下载,米琪手机铃声下载,艾米手机铃声下载...
  16. PS照片美化处理实例教程
  17. 云主机搭建Git服务器
  18. 文本相似度的那些算法
  19. 穿透代理获取用户真实IP地址
  20. 显示器连接服务器老一闪一闪,显示器黑屏一闪一闪的怎么回事_显示器黑屏一闪一闪的解决方法...

热门文章

  1. 2023山东国际养老服务业展览会/济南养老展/中国智慧养老展
  2. 双卡双待智能android,双号一体 双卡双待热门Android智能机推荐
  3. 英伟达“核弹”再次来袭?Web3.0最高8万招聘,周星驰也来了/文本生成图像引“掐架”……...
  4. 网络上计算机打不开怎么回事,电脑网页打不开怎么回事_电脑打不开网页的处理方法...
  5. 微信小程序 view的文本不能自动换行问题
  6. 【数据分析】基于时间序列的预测方法
  7. SpringDataElasticSearch - NativeSearchQueryBuilder过滤聚合高亮查询
  8. “翟天临”克星?新开发的反“枪手”AI,识别作业代写准确率接近90%
  9. pin、pad、port含义理解
  10. 如何检查 JavaScript 中的“未定义”? [复制]