以下是 Android 音乐软件在丢失音频焦点时停止播放的完整 Java 实现:
📱 完整实现代码
-
音频焦点管理器
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import android.util.Log;public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {
private static final String TAG = "AudioFocusManager";// 音频焦点状态回调接口 public interface AudioFocusCallback { void onAudioFocusGained(); // 获得焦点 void onAudioFocusLost(); // 永久失去焦点 void onAudioFocusLossTransient(); // 暂时失去焦点 void onAudioFocusDuck(); // 降低音量 } private AudioManager audioManager; private AudioFocusCallback callback; private AudioFocusRequest audioFocusRequest; // Android O+ 使用 private boolean hasAudioFocus = false; public AudioFocusManager(Context context) { this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); } public void setAudioFocusCallback(AudioFocusCallback callback) { this.callback = callback; } /** * 请求音频焦点 * @return 是否成功获得焦点 */ public boolean requestAudioFocus() { int result; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0+ 使用 AudioFocusRequest AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(audioAttributes) .setAcceptsDelayedFocusGain(true) .setWillPauseWhenDucked(true) .setOnAudioFocusChangeListener(this) .build(); result = audioManager.requestAudioFocus(audioFocusRequest); } else { // Android 8.0 以下 result = audioManager.requestAudioFocus( this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN ); } hasAudioFocus = (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED); Log.d(TAG, "Request audio focus: " + (hasAudioFocus ? "Granted" : "Denied")); return hasAudioFocus; } /** * 音频焦点变化监听 */ @Override public void onAudioFocusChange(int focusChange) { Log.d(TAG, "Audio focus changed: " + focusChangeToString(focusChange)); if (callback == null) { return; } switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: // 重新获得焦点 hasAudioFocus = true; callback.onAudioFocusGained(); break; case AudioManager.AUDIOFOCUS_LOSS: // 永久失去焦点(如来电、其他应用播放) hasAudioFocus = false; callback.onAudioFocusLost(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: // 暂时失去焦点 hasAudioFocus = false; callback.onAudioFocusLossTransient(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // 可降低音量的暂时失去焦点 hasAudioFocus = false; callback.onAudioFocusDuck(); break; } } /** * 释放音频焦点 */ public void abandonAudioFocus() { if (!hasAudioFocus) { return; } try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (audioFocusRequest != null) { audioManager.abandonAudioFocusRequest(audioFocusRequest); } } else { audioManager.abandonAudioFocus(this); } } catch (Exception e) { Log.e(TAG, "Error abandoning audio focus", e); } hasAudioFocus = false; Log.d(TAG, "Audio focus abandoned"); } public boolean hasAudioFocus() { return hasAudioFocus; } private String focusChangeToString(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: return "AUDIOFOCUS_GAIN"; case AudioManager.AUDIOFOCUS_LOSS: return "AUDIOFOCUS_LOSS"; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: return "AUDIOFOCUS_LOSS_TRANSIENT"; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: return "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; default: return "UNKNOWN(" + focusChange + ")"; } }}
-
音乐播放器(处理焦点丢失停止播放)
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.io.IOException;public class MusicPlayer {
private static final String TAG = "MusicPlayer";private MediaPlayer mediaPlayer; private AudioFocusManager audioFocusManager; private Handler mainHandler = new Handler(Looper.getMainLooper()); private PlayerStateListener playerStateListener; // 播放状态监听 public interface PlayerStateListener { void onPlaybackStarted(); void onPlaybackPaused(); void onPlaybackStopped(); void onPlaybackError(String error); } public MusicPlayer(AudioFocusManager audioFocusManager) { this.audioFocusManager = audioFocusManager; setupAudioFocusCallbacks(); } public void setPlayerStateListener(PlayerStateListener listener) { this.playerStateListener = listener; } private void setupAudioFocusCallbacks() { audioFocusManager.setAudioFocusCallback(new AudioFocusManager.AudioFocusCallback() { @Override public void onAudioFocusGained() { Log.i(TAG, "Audio focus gained, resuming playback"); resume(); } @Override public void onAudioFocusLost() { Log.i(TAG, "Audio focus lost, stopping playback"); // 永久失去焦点,完全停止播放 stop(); } @Override public void onAudioFocusLossTransient() { Log.i(TAG, "Audio focus lost transient, pausing playback"); // 暂时失去焦点,暂停播放 pause(); } @Override public void onAudioFocusDuck() { Log.i(TAG, "Audio focus duck, reducing volume"); // 降低音量 if (mediaPlayer != null) { mediaPlayer.setVolume(0.2f, 0.2f); // 3秒后恢复音量 mainHandler.postDelayed(() -> { if (mediaPlayer != null && audioFocusManager.hasAudioFocus()) { mediaPlayer.setVolume(1.0f, 1.0f); } }, 3000); } } }); } /** * 播放音乐 */ public void play(String audioPath) { if (audioFocusManager.requestAudioFocus()) { startPlayback(audioPath); } else { Log.w(TAG, "Failed to get audio focus, retrying..."); // 延迟重试获取焦点 mainHandler.postDelayed(() -> { if (audioFocusManager.requestAudioFocus()) { startPlayback(audioPath); } else { if (playerStateListener != null) { playerStateListener.onPlaybackError("无法获取音频焦点"); } } }, 500); } } private void startPlayback(String audioPath) { releaseMediaPlayer(); try { mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(audioPath); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnPreparedListener(mp -> { Log.i(TAG, "MediaPlayer prepared, starting playback"); mp.start(); if (playerStateListener != null) { playerStateListener.onPlaybackStarted(); } }); mediaPlayer.setOnCompletionListener(mp -> { Log.i(TAG, "Playback completed"); if (playerStateListener != null) { playerStateListener.onPlaybackStopped(); } audioFocusManager.abandonAudioFocus(); }); mediaPlayer.setOnErrorListener((mp, what, extra) -> { Log.e(TAG, "MediaPlayer error: what=" + what + ", extra=" + extra); if (playerStateListener != null) { playerStateListener.onPlaybackError("播放错误: " + what); } audioFocusManager.abandonAudioFocus(); return false; }); mediaPlayer.prepareAsync(); } catch (IOException e) { Log.e(TAG, "Failed to prepare MediaPlayer", e); if (playerStateListener != null) { playerStateListener.onPlaybackError("准备播放失败: " + e.getMessage()); } audioFocusManager.abandonAudioFocus(); } } /** * 暂停播放 */ public void pause() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); Log.i(TAG, "Playback paused"); if (playerStateListener != null) { playerStateListener.onPlaybackPaused(); } } } /** * 恢复播放 */ public void resume() { if (mediaPlayer != null && !mediaPlayer.isPlaying()) { mediaPlayer.start(); Log.i(TAG, "Playback resumed"); if (playerStateListener != null) { playerStateListener.onPlaybackStarted(); } } } /** * 停止播放(释放焦点) */ public void stop() { if (mediaPlayer != null) { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); Log.i(TAG, "Playback stopped"); if (playerStateListener != null) { playerStateListener.onPlaybackStopped(); } } } audioFocusManager.abandonAudioFocus(); } /** * 释放所有资源 */ public void release() { stop(); releaseMediaPlayer(); } private void releaseMediaPlayer() { if (mediaPlayer != null) { try { mediaPlayer.release(); } catch (Exception e) { Log.e(TAG, "Error releasing MediaPlayer", e); } mediaPlayer = null; } } public boolean isPlaying() { return mediaPlayer != null && mediaPlayer.isPlaying(); }}
-
Activity 中使用
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;public class MusicPlayerActivity extends AppCompatActivity {
private static final String TEST_AUDIO_URL = "https://example.com/song.mp3";private AudioFocusManager audioFocusManager; private MusicPlayer musicPlayer; private TextView statusText; private Button playButton, pauseButton, stopButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_music_player); // 初始化UI statusText = findViewById(R.id.status_text); playButton = findViewById(R.id.play_button); pauseButton = findViewById(R.id.pause_button); stopButton = findViewById(R.id.stop_button); // 初始化音频焦点管理器 audioFocusManager = new AudioFocusManager(this); // 初始化音乐播放器 musicPlayer = new MusicPlayer(audioFocusManager); musicPlayer.setPlayerStateListener(new MusicPlayer.PlayerStateListener() { @Override public void onPlaybackStarted() { runOnUiThread(() -> { statusText.setText("播放中..."); Toast.makeText(MusicPlayerActivity.this, "开始播放", Toast.LENGTH_SHORT).show(); }); } @Override public void onPlaybackPaused() { runOnUiThread(() -> { statusText.setText("已暂停"); Toast.makeText(MusicPlayerActivity.this, "播放暂停", Toast.LENGTH_SHORT).show(); }); } @Override public void onPlaybackStopped() { runOnUiThread(() -> { statusText.setText("已停止"); Toast.makeText(MusicPlayerActivity.this, "播放停止", Toast.LENGTH_SHORT).show(); }); } @Override public void onPlaybackError(String error) { runOnUiThread(() -> { statusText.setText("播放错误"); Toast.makeText(MusicPlayerActivity.this, error, Toast.LENGTH_LONG).show(); }); } }); // 设置按钮点击事件 playButton.setOnClickListener(v -> { musicPlayer.play(TEST_AUDIO_URL); }); pauseButton.setOnClickListener(v -> { musicPlayer.pause(); }); stopButton.setOnClickListener(v -> { musicPlayer.stop(); }); } @Override protected void onPause() { super.onPause(); // Activity 失去焦点时暂停播放 musicPlayer.pause(); } @Override protected void onDestroy() { super.onDestroy(); // 释放资源 musicPlayer.release(); }}
-
Service 中实现(后台播放)
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;public class MusicService extends Service {
private static final String TAG = "MusicService";
private static final String CHANNEL_ID = "music_channel";
private static final int NOTIFICATION_ID = 1;private MusicPlayer musicPlayer; private AudioFocusManager audioFocusManager; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "MusicService onCreate"); // 创建通知渠道 createNotificationChannel(); // 初始化音频焦点管理器 audioFocusManager = new AudioFocusManager(this); // 初始化播放器 musicPlayer = new MusicPlayer(audioFocusManager); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null) { String action = intent.getAction(); if (action != null) { switch (action) { case "PLAY": String audioUrl = intent.getStringExtra("audio_url"); if (audioUrl != null) { playMusic(audioUrl); } break; case "PAUSE": pauseMusic(); break; case "STOP": stopMusic(); break; } } } // 保持服务运行 return START_STICKY; } private void playMusic(String audioUrl) { musicPlayer.play(audioUrl); startForeground(NOTIFICATION_ID, createNotification("播放中")); } private void pauseMusic() { musicPlayer.pause(); updateNotification("已暂停"); } private void stopMusic() { musicPlayer.stop(); stopForeground(true); stopSelf(); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "MusicService onDestroy"); if (musicPlayer != null) { musicPlayer.release(); } } @Override public IBinder onBind(Intent intent) { return null; } // 创建通知渠道 private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "音乐播放", NotificationManager.IMPORTANCE_LOW ); channel.setDescription("音乐播放通知"); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } // 创建通知 private Notification createNotification(String text) { Notification.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new Notification.Builder(this, CHANNEL_ID); } else { builder = new Notification.Builder(this); } builder.setContentTitle("音乐播放器") .setContentText(text) .setSmallIcon(R.drawable.ic_music_note) .setOngoing(true); return builder.build(); } // 更新通知 private void updateNotification(String text) { Notification notification = createNotification(text); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); if (manager != null) { manager.notify(NOTIFICATION_ID, notification); } }}
🔧 关键要点
- 音频焦点丢失处理策略
• AUDIOFOCUS_LOSS:永久失去焦点 → 完全停止播放
• AUDIOFOCUS_LOSS_TRANSIENT:暂时失去焦点 → 暂停播放
• AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:可降低音量 → 音量降低到20%
- 资源管理
• 播放结束后自动释放音频焦点
• Activity/Service 销毁时清理资源
• 避免 MediaPlayer 内存泄漏
- 使用建议
• 播放前先请求焦点
• 焦点丢失时立即响应
• 播放完成或出错时释放焦点
• Service 中播放需要保持前台通知
这样实现后,你的音乐应用就能正确处理音频焦点,在来电或其他应用播放时自动暂停或停止播放,提供良好的用户体验。