MediaSession 没有从 MediaStyle 通知中获得任何回调
Posted
技术标签:
【中文标题】MediaSession 没有从 MediaStyle 通知中获得任何回调【英文标题】:MediaSession not getting any callbacks from MediaStyle Notification 【发布时间】:2019-03-08 02:39:39 【问题描述】:我创建了一个扩展 MediaBrowserServiceCompat 的服务。此服务包含对我的播放器的引用并创建一个带有回调的新 MediaSession。每次播放器更改状态时,我都会更新 MediaSession 的播放状态并创建一个 MediaStyle 通知。当我开始在我的播放器中播放某些内容时会显示通知,但通知中的按钮不会触发 MediaSession 回调,它们不会做任何事情。我在 MediaSession 中设置了正确的标志,我将会话设置为活动,我在播放状态下设置了正确的操作,我将会话令牌传递给通知,但仍然没有从中获得任何回调.我真的不知道我做错了什么。所有这些代码都在我的应用导入的模块中。
我的 NotificationHelper 类:
private final MusicService mService;
private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationCompat.Action mNextAction;
private final NotificationCompat.Action mPrevAction;
private final NotificationManager mNotificationManager;
public MediaNotificationManager(MusicService service)
mService = service;
mNotificationManager =
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
mPlayAction =
new NotificationCompat.Action(
R.drawable.exo_icon_play,
"Play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.exo_icon_pause,
"Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
mNextAction =
new NotificationCompat.Action(
R.drawable.exo_icon_next,
"Next",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
mPrevAction =
new NotificationCompat.Action(
R.drawable.exo_icon_previous,
"Previous",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
public Notification getNotification(MediaMetadataCompat metadata,
@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token)
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description)
// Create the (mandatory) notification channel when running on android Oreo.
if (isAndroidOOrHigher())
createChannel();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID)
.setSmallIcon(R.drawable.exo_notification_small_icon)
.setContentTitle("Track title")
.setContentText("Artist - Album")
.setLargeIcon(BitmapFactory.decodeResource(mService.getResources(), R.drawable.exo_notification_small_icon))
.setStyle(new MediaStyle().setShowActionsInCompactView(0).setMediaSession(token));
builder.addAction(mPrevAction);
builder.addAction(isPlaying ? mPauseAction : mPlayAction);
builder.addAction(mNextAction);
return builder;
// Does nothing on versions of Android earlier than O.
@RequiresApi(Build.VERSION_CODES.O)
private void createChannel()
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null)
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]100, 200, 300, 400, 500, 400, 300, 200, 400);
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
else
Log.d(TAG, "createChannel: Existing channel reused");
private boolean isAndroidOOrHigher()
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
我的服务类:
public class MusicService extends MediaBrowserServiceCompat
private static final String TAG = MusicService.class.getSimpleName();
private MediaSessionCompat mSession;
private PlayerManager playerManager;
private MediaSessionCallback mCallback;
private MediaNotificationManager mediaNotificationManager;
@Override
public void onCreate()
super.onCreate();
playerManager = PlayerManager.getInstance(this);
playerManager.addListener(new PlayerManagerServiceListener());
mediaNotificationManager = new MediaNotificationManager(this);
// Create a new MediaSession.
mSession = new MediaSessionCompat(this, "MusicService");
mCallback = new MediaSessionCallback();
mSession.setCallback(mCallback);
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
setSessionToken(mSession.getSessionToken());
mSession.setActive(true);
@Override
public void onDestroy()
mSession.release();
Log.d(TAG, "onDestroy: MediaPlayerAdapter stopped, and MediaSession released");
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName,
int clientUid,
Bundle rootHints)
return new BrowserRoot("root", null);
@Override
public void onLoadChildren(
@NonNull final String parentMediaId,
@NonNull final Result<List<MediaBrowserCompat.MediaItem>> result)
result.sendResult(null);
// MediaSession Callback: Transport Controls -> MediaPlayerAdapter
public class MediaSessionCallback extends MediaSessionCompat.Callback
@Override
public void onPlay()
playerManager.play();
@Override
public void onPause()
playerManager.pause();
@Override
public void onStop()
playerManager.stop();
@Override
public void onSkipToNext()
playerManager.next();
@Override
public void onSkipToPrevious()
playerManager.previous();
@Override
public void onSeekTo(long pos)
playerManager.seekTo(pos);
public class PlayerManagerServiceListener implements PlayerManager.PlayerManagerListener
@Override
public void onError(@Nullable Exception error)
@Override
public void onProgress(long duration, long position)
@Override
public void onPlayerChange(int change)
@Override
public void onTrackChange(TrackVO track)
@Override
public void onListChange(List tracks)
@Override
public void onPlaybackStateChange(int playbackState)
PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder();
int playbackStateCompat = -1;
switch(playbackState)
case PlaybackStateListener.STATE_PLAYING:
playbackStateCompat = PlaybackStateCompat.STATE_PLAYING;
//playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE);
break;
case PlaybackStateListener.STATE_PAUSED:
playbackStateCompat = PlaybackStateCompat.STATE_PAUSED;
//playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY);
break;
if (playbackStateCompat == -1)
return;
mSession.setActive(true);
playbackstateBuilder.setActions(
PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PLAY_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
playbackstateBuilder.setState(playbackStateCompat, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
PlaybackStateCompat state = playbackstateBuilder.build();
MediaMetadataCompat mediaMetadata = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, playerManager.getCurrenTrack().getName())
.build();
mSession.setMetadata(mediaMetadata);
mSession.setPlaybackState(state);
Notification notification = mediaNotificationManager.getNotification(
mediaMetadata,
state,
getSessionToken()
);
Intent intent = new Intent(MusicService.this, MusicService.class);
ContextCompat.startForegroundService(MusicService.this, intent);
startForeground(417, notification);
MediaBrowserHelper 初始化服务:
public class MediaBrowserHelper
private static final String TAG = MediaBrowserHelper.class.getSimpleName();
private final Context mContext;
private final Class<? extends MediaBrowserServiceCompat> mMediaBrowserServiceClass;
private final List<Callback> mCallbackList = new ArrayList<>();
private final MediaBrowserConnectionCallback mMediaBrowserConnectionCallback;
private final MediaControllerCallback mMediaControllerCallback;
private final MediaBrowserSubscriptionCallback mMediaBrowserSubscriptionCallback;
private MediaBrowserCompat mMediaBrowser;
@Nullable
private MediaControllerCompat mMediaController;
public MediaBrowserHelper(Context context,
Class<? extends MediaBrowserServiceCompat> serviceClass)
mContext = context;
mMediaBrowserServiceClass = serviceClass;
mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
mMediaControllerCallback = new MediaControllerCallback();
mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
public void onStart()
if (mMediaBrowser == null)
mMediaBrowser =
new MediaBrowserCompat(
mContext,
new ComponentName(mContext, mMediaBrowserServiceClass),
mMediaBrowserConnectionCallback,
null);
mMediaBrowser.connect();
Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
public void onStop()
if (mMediaController != null)
mMediaController.unregisterCallback(mMediaControllerCallback);
mMediaController = null;
if (mMediaBrowser != null && mMediaBrowser.isConnected())
mMediaBrowser.disconnect();
mMediaBrowser = null;
resetState();
Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
/**
* Called after connecting with a @link MediaBrowserServiceCompat.
* <p>
* Override to perform processing after a connection is established.
*
* @param mediaController @link MediaControllerCompat associated with the connected
* MediaSession.
*/
protected void onConnected(@NonNull MediaControllerCompat mediaController)
/**
* Called after loading a browsable @link MediaBrowserCompat.MediaItem
*
* @param parentId The media ID of the parent item.
* @param children List (possibly empty) of child items.
*/
protected void onChildrenLoaded(@NonNull String parentId,
@NonNull List<MediaBrowserCompat.MediaItem> children)
/**
* Called when the @link MediaBrowserServiceCompat connection is lost.
*/
protected void onDisconnected()
@NonNull
protected final MediaControllerCompat getMediaController()
if (mMediaController == null)
throw new IllegalStateException("MediaController is null!");
return mMediaController;
/**
* The internal state of the app needs to revert to what it looks like when it started before
* any connections to the @link MusicService happens via the @link MediaSessionCompat.
*/
private void resetState()
performOnAllCallbacks(new CallbackCommand()
@Override
public void perform(@NonNull Callback callback)
callback.onPlaybackStateChanged(null);
);
Log.d(TAG, "resetState: ");
public MediaControllerCompat.TransportControls getTransportControls()
if (mMediaController == null)
Log.d(TAG, "getTransportControls: MediaController is null!");
throw new IllegalStateException("MediaController is null!");
return mMediaController.getTransportControls();
public void registerCallback(Callback callback)
if (callback != null)
mCallbackList.add(callback);
// Update with the latest metadata/playback state.
if (mMediaController != null)
final MediaMetadataCompat metadata = mMediaController.getMetadata();
if (metadata != null)
callback.onMetadataChanged(metadata);
final PlaybackStateCompat playbackState = mMediaController.getPlaybackState();
if (playbackState != null)
callback.onPlaybackStateChanged(playbackState);
private void performOnAllCallbacks(@NonNull CallbackCommand command)
for (Callback callback : mCallbackList)
if (callback != null)
command.perform(callback);
/**
* Helper for more easily performing operations on all listening clients.
*/
private interface CallbackCommand
void perform(@NonNull Callback callback);
// Receives callbacks from the MediaBrowser when it has successfully connected to the
// MediaBrowserService (MusicService).
private class MediaBrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback
// Happens as a result of onStart().
@Override
public void onConnected()
try
// Get a MediaController for the MediaSession.
mMediaController =
new MediaControllerCompat(mContext, mMediaBrowser.getSessionToken());
mMediaController.registerCallback(mMediaControllerCallback);
// Sync existing MediaSession state to the UI.
mMediaControllerCallback.onMetadataChanged(mMediaController.getMetadata());
mMediaControllerCallback.onPlaybackStateChanged(
mMediaController.getPlaybackState());
MediaBrowserHelper.this.onConnected(mMediaController);
catch (RemoteException e)
Log.d(TAG, String.format("onConnected: Problem: %s", e.toString()));
throw new RuntimeException(e);
mMediaBrowser.subscribe(mMediaBrowser.getRoot(), mMediaBrowserSubscriptionCallback);
// Receives callbacks from the MediaBrowser when the MediaBrowserService has loaded new media
// that is ready for playback.
public class MediaBrowserSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback
@Override
public void onChildrenLoaded(@NonNull String parentId,
@NonNull List<MediaBrowserCompat.MediaItem> children)
MediaBrowserHelper.this.onChildrenLoaded(parentId, children);
// Receives callbacks from the MediaController and updates the UI state,
// i.e.: Which is the current item, whether it's playing or paused, etc.
private class MediaControllerCallback extends MediaControllerCompat.Callback
@Override
public void onMetadataChanged(final MediaMetadataCompat metadata)
performOnAllCallbacks(new CallbackCommand()
@Override
public void perform(@NonNull Callback callback)
callback.onMetadataChanged(metadata);
);
@Override
public void onPlaybackStateChanged(@Nullable final PlaybackStateCompat state)
performOnAllCallbacks(new CallbackCommand()
@Override
public void perform(@NonNull Callback callback)
callback.onPlaybackStateChanged(state);
);
// This might happen if the MusicService is killed while the Activity is in the
// foreground and onStart() has been called (but not onStop()).
@Override
public void onSessionDestroyed()
resetState();
onPlaybackStateChanged(null);
MediaBrowserHelper.this.onDisconnected();
清单:
<service android:name="com.amco.playermanager.MusicService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
【问题讨论】:
【参考方案1】:事实证明,整个问题是由另一个 BroadcastReceiver 处理我的应用程序清单中声明的 MEDIA_BUTTON 引起的。通过移除该接收器,现在一切正常。
【讨论】:
以上是关于MediaSession 没有从 MediaStyle 通知中获得任何回调的主要内容,如果未能解决你的问题,请参考以下文章
MediaSession和MediaSessionService的交互流程梳理
MediaSession和MediaSessionService的交互流程梳理
MediaSession和MediaSessionService的交互流程梳理