You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓后台音乐播放器开发求助:切出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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:28:50