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

MediaBrowserServiceCompat后台运行约10分钟后停止问题求助

问题描述

我开发了一款基于MediaBrowserServiceCompat和ExoPlayer的媒体应用,使用带前台通知的前台服务,理论上应允许服务在后台持续运行,但遇到以下问题:

  • 应用前台运行时一切正常,后台运行约10分钟后服务会停止,排除代码崩溃(前台未出现崩溃情况)
  • 测试其他音频应用及最小化媒体服务Demo均无此问题,推测是应用特有问题,系统未将其识别为合法前台服务
  • 异常现象:前台通知保留,应用似冻结状态,重启为冷启动无法快速恢复
  • 测试环境:Android 11,已禁用电池优化,强制Doze/Standby模式未复现问题

服务代码

public class MediaPlayerService extends MediaBrowserServiceCompat {

    private MediaSessionCompat mMediaSessionCompat;
    private ExoPlayer mPlayer;

   @Override
    public void onCreate() {
        super.onCreate();
        
        mMediaSessionCompat = new MediaSessionCompat(this, MediaPlayerService.class.getSimpleName());
        mMediaSessionCompat.setCallback(mMediaSessionCallback);
        mMediaSessionCompat.setActive(true);
        
        setSessionToken(mMediaSessionCompat.getSessionToken());
    }
    
    
     private MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() {
        
        
        @Override
        public void onPlay() {
            super.onPlay();

            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
            notificationManager.createNotificationChannel(new NotificationChannel("channelid", "Channel Name", NotificationManager.IMPORTANCE_DEFAULT));

            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channelid")
                        ...
                        .setSmallIcon(R.drawable.ic_notification);

            startForegroundService(new Intent(getApplicationContext(), MediaPlayerService.class));
            startForeground(101, builder.build());
            
            mPlayer = new ExoPlayer.Builder(this).build();
            mPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
            
            mPlayer.setMediaItem(MediaItem.fromUri(GetItemUri()));
            mPlayer.prepare();
            mPlayer.play();

        }

        @Override
        public void onPause() {
            super.onPause();
            
            Log.d(getPackageName(), "audio paused");

            mPlayer.pause();
            stopForeground(STOP_FOREGROUND_DETACH);
        }
        
        @Override
        public void onStop() {
            super.onStop();

            Log.d(getPackageName(), "stop stopped");

            mPlayer.stop();
            stopForeground(STOP_FOREGROUND_REMOVE);
        }

        
    };
    
    @Override
    public void onDestroy() {
        super.onDestroy();

        Log.d(getPackageName(), "service destroyed");

        mPlayer.releease();
        stopForeground(STOP_FOREGROUND_REMOVE);
    }

}

AndroidManifest配置

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    <service
        android:name=".MediaPlayerService"
        android:exported="true"
        android:foregroundServiceType="mediaPlayback"
        android:permission="android.permission.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK">
        <intent-filter>
            <action android:name="android.media.AUDIO_BECOMING_NOISY" />
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>

排查与修复方案

从代码和配置中可以定位到几个核心问题,逐一修正即可解决服务被后台回收的问题:

1. 前台服务启动逻辑错误

在onPlay()中调用startForegroundService()完全多余,且Android要求服务启动后5秒内必须调用startForeground(),当前调用时机不符合规范:

  • 触发onPlay时服务已处于运行状态,无需重复启动前台服务
  • 应在onCreate()中完成前台通知初始化,或服务启动后立即调用startForeground(),而非等到播放回调触发

2. ExoPlayer生命周期与配置问题

  • mPlayer在onPlay()中每次创建新实例,未做复用,容易引发资源泄漏
  • onDestroy()中mPlayer.releease()是拼写错误,应为release(),会导致播放器资源无法正确释放,触发系统回收
  • 未设置AudioAttributes,系统无法识别这是媒体播放服务,降低前台优先级:
    AudioAttributes audioAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
    mPlayer.setAudioAttributes(audioAttributes, true);
    
  • WakeMode设置错误:C.WAKE_MODE_NETWORK仅用于保持网络唤醒,媒体播放应使用C.WAKE_MODE_LOCAL保持CPU唤醒,避免后台休眠

3. 前台通知未关联媒体会话

系统需要通过媒体会话识别这是合法的媒体播放前台通知,否则可能判定为无效前台服务:

  • 在通知Builder中添加setMediaSession(mMediaSessionCompat.getSessionToken())
  • 通知渠道优先级应改为IMPORTANCE_LOW(媒体通知推荐级别),避免不必要的系统拦截

4. 服务生命周期回调不合理

onPause()中调用stopForeground(STOP_FOREGROUND_DETACH)会将通知与服务分离,此时服务失去前台标识,容易被系统回收:

  • 暂停时应保持前台状态,仅更新通知内容;仅在onStop()或onDestroy()时移除通知

修正后的核心代码示例

调整onCreate()初始化逻辑

@Override
public void onCreate() {
    super.onCreate();
    
    // 初始化媒体会话
    mMediaSessionCompat = new MediaSessionCompat(this, MediaPlayerService.class.getSimpleName());
    mMediaSessionCompat.setCallback(mMediaSessionCallback);
    mMediaSessionCompat.setActive(true);
    setSessionToken(mMediaSessionCompat.getSessionToken());

    // 初始化通知渠道
    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
    NotificationChannel channel = new NotificationChannel("channelid", "媒体播放", NotificationManager.IMPORTANCE_LOW);
    notificationManager.createNotificationChannel(channel);

    // 初始化ExoPlayer
    mPlayer = new ExoPlayer.Builder(this).build();
    AudioAttributes audioAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
    mPlayer.setAudioAttributes(audioAttributes, true);
    mPlayer.setWakeMode(C.WAKE_MODE_LOCAL);
}

修正onPlay()逻辑

@Override
public void onPlay() {
    super.onPlay();

    // 构建关联媒体会话的前台通知
    NotificationCompat.Builder builder = new NotificationCompat.Builder(MediaPlayerService.this, "channelid")
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle("正在播放")
        .setContentText("媒体标题")
        .setMediaSession(mMediaSessionCompat.getSessionToken())
        .setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
            .setMediaSession(mMediaSessionCompat.getSessionToken())
            .setShowActionsInCompactView(0,1)) // 添加播放控制按钮
        .setPriority(NotificationCompat.PRIORITY_LOW);

    startForeground(101, builder.build());

    // 准备并播放媒体
    mPlayer.setMediaItem(MediaItem.fromUri(GetItemUri()));
    mPlayer.prepare();
    mPlayer.play();
}

修正onDestroy()资源释放

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d(getPackageName(), "service destroyed");
    if (mPlayer != null) {
        mPlayer.release();
        mPlayer = null;
    }
    mMediaSessionCompat.setActive(false);
    stopForeground(STOP_FOREGROUND_REMOVE);
}

内容的提问来源于stack exchange,提问作者Kris B

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 11:43:17