Android-Audio-根据音频焦点控制播放

以下是 Android 音乐软件在丢失音频焦点时停止播放的完整 Java 实现:

📱 完整实现代码

  1. 音频焦点管理器

    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 + ")";
         }
     }

    }

  2. 音乐播放器(处理焦点丢失停止播放)

    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();
     }

    }

  3. 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();
     }

    }

  4. 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);
         }
     }

    }

🔧 关键要点

  1. 音频焦点丢失处理策略

• AUDIOFOCUS_LOSS:永久失去焦点 → 完全停止播放

• AUDIOFOCUS_LOSS_TRANSIENT:暂时失去焦点 → 暂停播放

• AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:可降低音量 → 音量降低到20%

  1. 资源管理

• 播放结束后自动释放音频焦点

• Activity/Service 销毁时清理资源

• 避免 MediaPlayer 内存泄漏

  1. 使用建议

• 播放前先请求焦点

• 焦点丢失时立即响应

• 播放完成或出错时释放焦点

• Service 中播放需要保持前台通知

这样实现后,你的音乐应用就能正确处理音频焦点,在来电或其他应用播放时自动暂停或停止播放,提供良好的用户体验。

相关推荐
brahmsjiang2 小时前
Java类加载机制解析:从JVM启动到双亲委派,再到Android的特殊实现
android·java·jvm
fire-flyer2 小时前
ClickHouse系列(九):慢查询、内存 OOM 与稳定性治理
android·clickhouse
SharpCJ11 小时前
Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革
android·ai·aigc
_李小白11 小时前
【OSG学习笔记】Day 38: TextureVisitor(纹理访问器)
android·笔记·学习
JJay.11 小时前
Kotlin 高阶函数学习指南
android·开发语言·kotlin
jinanwuhuaguo12 小时前
截止到4月8日,OpenClaw 2026年4月更新深度解读剖析:从“能力回归”到“信任内建”的范式跃迁
android·开发语言·人工智能·深度学习·kotlin
JJay.12 小时前
Android Kotlin 协程使用指南
android·开发语言·kotlin
BLUcoding13 小时前
Android 布局介绍
android
summerkissyou198713 小时前
android-蓝牙-状态和协议值总结及监听例子
android·蓝牙