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
相关产品推荐
相关产品推荐

