服务停止时 Widget 的 PendingIntent 丢失

PendingIntent from Widget lost when Service is stopped

我有一个带有 MusicPlayingService(扩展 MediaBrowserServiceCompat)的音乐播放器应用程序和一个用于控制该服务的小部件。 MusicPlayingService 还引用了 MusicStateManager(在 MusicServices onCreate 中创建),它实现“MediaSessionCompat.Callback”并处理来自当前 MediaSession 的所有回调:mSession.setCallback(MusicStateManager);

public class MusicPlayingService extends MediaBrowserServiceCompat implements
    AudioManager.OnAudioFocusChangeListener {

@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG, "MusicPlayingService started");

    .....

    mPlayerStateManager = new MusicStateManager(this);

    .....
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "onStartCommand");
    MediaButtonReceiver.handleIntent(mPlayerStateManager.getSession(), intent);
    return START_STICKY;
}

然后我的 Widget 有一堆 Pending Intent,它们被发送到 MusicPlayingService 并由 MusicStateManager 处理。一切都很好。

public class MusicStateManager extends MediaSessionCompat.Callback {

....

public MusicStateManager(@NonNull MusicStateManager argService) {
    Log.d(TAG, "setting service");
    mPlayerService = argService;

    ComponentName mediaButtonReceiver = new ComponentName(mPlayerService, HeadsetReceiver.class);
    mSession = new MediaSessionCompat(mPlayerService, SESSION_TAG, mediaButtonReceiver, null);
    mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
            MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    PendingIntent pendingMediaButtonIntent = PendingIntent.getBroadcast(mPlayerService, 0, mediaButtonIntent, 0);
    mSession.setMediaButtonReceiver(pendingMediaButtonIntent);

    Intent toggleIntent = new Intent(NotificationPlayer.toggleAction);
    PendingIntent pendingToggleIntent = PendingIntent.getBroadcast(mPlayerService, 0, toggleIntent, 0);
    mSession.setMediaButtonReceiver(pendingToggleIntent);

    mSession.setCallback(this);
    mSession.setActive(true);

}

   /**
     * Callback method called from MusicStateManager whenever the music is about to play.
     */
    public void onPlay() {
        Log.d(TAG, "onPlay");

        .......
    }

    @Override
    public void onCustomAction(String action, Bundle extras) {
        Log.d(TAG, "received action: " + action); // NoI18N

        if (ACTION_TOGGLE.equals(action)) {
            mPlayerService.toggle();
        }
    }

事情是这样的。如果我强制停止应用程序,音乐服务将关闭(很明显)。如果我然后按下小部件上的按钮,它会再次启动我的服务(我可以从 LogCat 中的各种 Log.d(...) 中看到它。但是,小部件的 PendingIntent 丢失了,并且从未处理过。

这对用户有奇怪的影响。大多数情况下,小部件上的按钮都可以使用(当服务为 运行 时)。但是,有时(当服务不是 运行 时)按钮在第一次按下时不会执行任何操作,但如果您再次按下它们,它们就会起作用(因为第一次按下启动了服务,但未能处理意图。

知道如何调试这个问题并查看我的 PendingIntent 会发生什么吗?

为媒体播放创建 PendingIntent 的最佳方法是使用 MediaButtonReceiver.buildMediaButtonPendingIntent()。这允许您传入一个 PlaybackStateCompat.ACTION_ 常量来触发正确的回调。

PendingIntent playPendingIntent = MediaButtonReceiver
  .buildMediaButtonPendingIntent(context, PlaybackStateCompat.ACTION_PLAY);

您似乎已经在 onStartCommand() 中进行了正确的 MediaButtonReceiver.handleIntent() 调用,但您还应确保在创建 MediaSessionCompat 时通过调用 [=] 启用正确的操作17=]:

// Ideally, you should keep a reference to this Builder to update it,
// rather than create it from scratch each time
PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
  .setActions(PlaybackStateCompat.ACTION_PLAY |
              PlaybackStateCompat.ACTION_PLAY_PAUSE);
mSession.setPlaybackState(playbackStateBuilder.build());

As MediaSessionCompat 默认情况下不允许任何媒体按钮。通过确保直接从实例化支持 ACTION_PLAY,您可以确保第一个 MediaButtonReceiver.handleIntent() 正确调用您的 MediaSessionCompat.Callback.