安卓后台音乐播放器开发求助:切出APP后MediaPlayer停止播放问题
解决后台Service中MediaPlayer切出APP停止播放的问题
Hey there! I see the issue with your Music Service — let's break down what's going wrong and fix it so your audio keeps playing even when the app is in the background.
你的代码里存在的几个关键问题:
initPlayer()从未被调用:你的mediaPlayer对象一直是null,直接调用start()会导致空指针异常(可能你实际运行时已经调用了,但代码里没体现)。- MediaPlayer未完成准备就播放:
setDataSource()之后必须调用prepare()或prepareAsync()才能启动播放,否则会报错。 - 普通后台Service容易被系统回收:从Android 8.0(API 26)开始,系统对后台Service的限制非常严格,普通后台Service很容易被系统杀死,导致播放停止。
- 未处理音频焦点:如果其他应用(比如浏览器、视频APP)抢占了音频焦点,你的MediaPlayer会自动停止,需要监听音频焦点变化来处理播放状态。
- 未正确释放MediaPlayer资源:
onDestroy()里没有释放MediaPlayer,会造成内存泄漏。
修改后的完整代码
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Build; import android.os.IBinder; import android.provider.Settings; import androidx.core.app.NotificationCompat; import java.io.IOException; public class MusicService extends Service implements MediaPlayer.OnPreparedListener, AudioManager.OnAudioFocusChangeListener { private MediaPlayer mediaPlayer; private AudioManager audioManager; private AudioFocusRequest audioFocusRequest; private static final String CHANNEL_ID = "MusicPlayerChannel"; private static final int NOTIFICATION_ID = 1001; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); initAudioFocus(); initPlayer(); createNotificationChannel(); } // 初始化音频焦点 private void initAudioFocus() { audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener(this) .build(); } } // 初始化MediaPlayer private void initPlayer() { if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnPreparedListener(this); // 设置准备完成监听 try { mediaPlayer.setDataSource(this, Settings.System.DEFAULT_RINGTONE_URI); mediaPlayer.prepareAsync(); // 异步准备,避免阻塞主线程 } catch (IOException e) { e.printStackTrace(); } } } // 创建通知渠道(Android 8.0+ 必须) private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "Music Player Service", NotificationManager.IMPORTANCE_LOW // 低优先级,不会弹出提示 ); NotificationManager manager = getSystemService(NotificationManager.class); if (manager != null) { manager.createNotificationChannel(channel); } } } // 启动前台Service,显示通知 private void startForegroundService() { // 创建点击通知返回APP的Intent Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("正在播放音乐") .setContentText("后台持续播放中") .setSmallIcon(R.drawable.ic_music_note) // 替换成你的应用图标 .setContentIntent(pendingIntent) .setOngoing(true) // 设置为不可滑动删除 .build(); startForeground(NOTIFICATION_ID, notification); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null && intent.getAction() != null) { switch (intent.getAction()) { case "com.play": requestAudioFocusAndPlay(); break; } } return START_STICKY; // 服务被系统杀死后尝试重启 } // 请求音频焦点并播放 private void requestAudioFocusAndPlay() { int result; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { result = audioManager.requestAudioFocus(audioFocusRequest); } else { result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 启动前台Service提升优先级 startForegroundService(); if (mediaPlayer != null && !mediaPlayer.isPlaying()) { mediaPlayer.start(); } } } // MediaPlayer准备完成回调 @Override public void onPrepared(MediaPlayer mp) { // 准备完成后可以在这里自动播放,或者等待外部指令触发 } // 音频焦点变化回调 @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_LOSS: // 永久失去焦点,停止播放并释放资源 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } stopForeground(true); stopSelf(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: // 暂时失去焦点,暂停播放 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // 暂时失去焦点,但可以继续播放(降低音量) if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.setVolume(0.3f, 0.3f); } break; case AudioManager.AUDIOFOCUS_GAIN: // 重新获得焦点,恢复播放或音量 if (mediaPlayer != null) { mediaPlayer.setVolume(1.0f, 1.0f); if (!mediaPlayer.isPlaying()) { mediaPlayer.start(); } } break; } } @Override public void onDestroy() { super.onDestroy(); // 释放MediaPlayer资源 if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } // 放弃音频焦点 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { audioManager.abandonAudioFocusRequest(audioFocusRequest); } else { audioManager.abandonAudioFocus(this); } stopForeground(true); } }
额外注意事项:
- 权限声明:在
AndroidManifest.xml中添加必要的权限:<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 可选,防止设备休眠导致播放停止 --> - 注册Service:在
AndroidManifest.xml中注册你的Service:<service android:name=".MusicService" android:foregroundServiceType="mediaPlayback" <!-- Android 12+ 必须指定类型 --> android:exported="false" /> - 启动Service的方式:从Activity启动Service时,注意适配Android 8.0+的后台启动限制:
Intent intent = new Intent(this, MusicService.class); intent.setAction("com.play"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(intent); } else { startService(intent); }
这样修改后,你的Service会以前台Service运行,拥有更高的系统优先级,不容易被回收,同时正确处理了MediaPlayer的生命周期和音频焦点,就能实现切出APP后持续播放的需求了。
内容的提问来源于stack exchange,提问作者Sánthösh Kümár
相关产品推荐
相关产品推荐

