Media Session Compat在Lollipop之前版本无法显示锁屏控制

41

我正在使用AppCompat支持库版本22中的MediaSessionCompat。在Lollipop上,我收到了通知,并且锁屏的背景是专辑封面。一切都很顺利。

而在Pre-Lollipop设备上,锁屏上的音乐控件根本没有显示出来。这很奇怪,我尝试了各种方法,但它并没有显示出来,甚至连背景也没有改变。

我希望有人能解决这个问题。

注意:RemoteControlClient曾在Lollipop和KitKat上工作。

/**
 * Initializes the remote control client
 */
private void setupMediaSession() {
    /* Activate Audio Manager */
    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN);

    ComponentName mRemoteControlResponder = new ComponentName(getPackageName(),
            MediaButtonReceiver.class.getName());
    final Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    mediaButtonIntent.setComponent(mRemoteControlResponder);
    mMediaSessionCompat = new MediaSessionCompat(getApplication(), "JairSession", mRemoteControlResponder, null);
    mMediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    PlaybackStateCompat playbackStateCompat = new PlaybackStateCompat.Builder()
            .setActions(
                    PlaybackStateCompat.ACTION_SEEK_TO |
                    PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
                    PlaybackStateCompat.ACTION_PLAY |
                    PlaybackStateCompat.ACTION_PAUSE |
                    PlaybackStateCompat.ACTION_STOP
            )
            .setState(
                    isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
                    getCurrentPosition(),
                    1.0f)
            .build();
    mMediaSessionCompat.setPlaybackState(playbackStateCompat);
    mMediaSessionCompat.setCallback(mMediaSessionCallback);
    mMediaSessionCompat.setSessionActivity(retrievePlaybackActions(5));
    mMediaSessionCompat.setActive(true);
    updateMediaSessionMetaData();
    mTransportController = mMediaSessionCompat.getController().getTransportControls();
这里是 updateMediaSessionMetaData() 函数:
/**
 * Updates the lockscreen controls, if enabled.
 */
private void updateMediaSessionMetaData() {
            MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
            builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, getArtistName());
            builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, getAlbumName());
            builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, getTrackName());
            builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, getDuration());
            builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, MusicUtils.getArtwork(this, getAlbumID(), true));
            mMediaSessionCompat.setMetadata(builder.build());

媒体会话回调方法

private final MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() {

    @Override
    public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
        final String intentAction = mediaButtonEvent.getAction();
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
            if (PrefUtils.isHeadsetPause(getBaseContext())) {
                Log.d(LOG_TAG, "Headset disconnected");
                pause();
            }
        } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
            final KeyEvent event = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (event == null) return super.onMediaButtonEvent(mediaButtonEvent);
            final int keycode = event.getKeyCode();
            final int action = event.getAction();
            final long eventTime = event.getEventTime();
            if (event.getRepeatCount() == 0 && action == KeyEvent.ACTION_DOWN) {
                switch (keycode) {
                    case KeyEvent.KEYCODE_HEADSETHOOK:
                        if (eventTime - mLastClickTime < DOUBLE_CLICK) {
                            playNext(mSongNumber);
                            mLastClickTime = 0;
                        } else {
                            if (isPlaying())
                                pause();
                            else resume();
                            mLastClickTime = eventTime;
                        }
                        break;
                    case KeyEvent.KEYCODE_MEDIA_STOP:
                        mTransportController.stop();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                        if (isMediaPlayerActive()) {
                            if (isPlaying()) mTransportController.pause();
                            else mTransportController.play();
                        }
                        break;
                    case KeyEvent.KEYCODE_MEDIA_NEXT:
                        mTransportController.skipToNext();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                        mTransportController.skipToPrevious();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PAUSE:
                        mTransportController.pause();
                        break;
                    case KeyEvent.KEYCODE_MEDIA_PLAY:
                        mTransportController.play();
                        break;
                }
            }
        }
        return super.onMediaButtonEvent(mediaButtonEvent);
    }

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

    @Override
    public void onPause() {
        super.onPause();
        pause();
    }

    @Override
    public void onSkipToNext() {
        super.onSkipToNext();
        playNext(mSongNumber);
    }

    @Override
    public void onSkipToPrevious() {
        super.onSkipToPrevious();
        playPrevious(mSongNumber);
    }

    @Override
    public void onSeekTo(long pos) {
        super.onSeekTo(pos);
        seekTo(pos);
    }

    @Override
    public void onStop() {
        super.onStop();
        pause();
        commitMusicData();
        updatePlayingUI(STOP_ACTION);
        stopSelf();
    }
};
媒体按钮接收器清单条目。
<!-- Media button receiver -->
    <receiver android:name=".receiver.MediaButtonReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
            <action android:name="android.media.AUDIO_BECOMING_NOISY" />
        </intent-filter>
    </receiver>

我已经尝试了几周来解决这个问题,但一直没有成功,非常需要帮助。

编辑:有关MediaSessionCompat的教程或示例也可以。



@ianhanniballake 添加了代码 - Akshay Chordiya
@ianhanniballake 我使用了Notifications.MediaStyle,但在Pre-Lollipop设备上无法工作。 - Akshay Chordiya
你使用的Support Library版本是什么? - ianhanniballake
最新版本为22.2.0 - 你能更新到这个版本并确认问题是否仍然存在吗? - ianhanniballake
@ianhanniballake,在上面的评论中我打错了一个字,我使用的是v22.2.0版本。但在“棒棒糖”上可以运行。 - Akshay Chordiya
显示剩余14条评论
3个回答

11

虽然在使用MediaSession时不是严格要求,但在API14-19设备上使用的RemoteControlClient需要音频焦点,并且对于所有媒体播放强烈建议使用。

可以添加以下内容:

AudioManager audioManager = (AudioManager)
    getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this,
    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_GAIN) {
    return; //Failed to gain audio focus
}
在播放任何媒体之前,应获得音频焦点并显示控件。

我已经在使用它,但是我之前是在MediaSessionCompat下使用它的,现在我将AudioManager代码移到了它上面。但是在模拟器上好像还是不起作用。 - Akshay Chordiya
你需要在你的问题中包含更多的代码,最好包括整个服务和媒体按钮接收器的清单条目。 - ianhanniballake
@RisingUp 我觉得我是唯一面临这个问题的人。希望我们能解决它。 - Akshay Chordiya
2
你的代码在我的 API 16 模拟器上运行良好 - 查看这个超级最小示例,展示了我设备上的锁屏控制。 - ianhanniballake
@Aky - 我所拥有的仅有 INTERNETACCESS_NETWORK_STATE 权限,用于下载我测试的 MP3 文件。我甚至将其从示例中剥离了出来(实际上示例中根本没有播放 - 只是模拟)。我注意到在真实设备上,一般情况下事情都比模拟器要好得多,因此也许模拟器并不适合测试这些类型的东西。 - ianhanniballake
显示剩余12条评论

9

最后我找到了一个解决方案。感谢 @ianhanniballake@user1549672

  1. 按照@ianhanniballake的建议添加音频焦点
  2. 可以在Google上搜索,并且还可以在官方Android文档中找到Music Intent BroadcastReceiver
  3. 编写我上面问题中提供的setupMediaSession()
  4. 非常重要正确地提及Flags
  5. 编写也在上面提供的MediaSessionCallbacks
  6. 非常重要MediaPlayer#onPause()MediaPlayer#onStart()以及播放新歌曲时(包括下一首和上一首)更新MediaSession,如@user1549672所述。
  7. onDestory()中释放MediaSession对象。

好的,以上大部分材料(代码)都可以获取。这个问题花费了几个月的时间来解决,终于完成了。


兄弟,你是如何显示锁屏媒体控制器的? - SAVVY
@SAVVY 我认为这行代码告诉 Android 操作系统在锁屏界面上显示音乐控制器。mMediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); - Akshay Chordiya
兄弟,你能告诉我我哪里做错了吗?我无法得到锁屏界面。https://dev59.com/caPia4cB1Zd3GeqP16mj - SAVVY
兄弟,我按照你说的做了,但不知道哪里出错了。悬赏还在进行中,但仍然没有答案。希望你能指导我。 - SAVVY
1
@SAVVY 很好。要显示其他按钮,您需要使用 mMediaSessionCompat.setPlaybackState(new PlaybackStateCompat.Builder() .setState(playState, position(), 1.0f) .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE| PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).build()); 设置操作。 - Akshay Chordiya
显示剩余2条评论

5

最后我得到了解答,关于你和我的问题。问题在于,即使更新媒体会话(MediaSessionCompat),你仍需要指定操作(mMediaSessionCompat.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE))。因此,你的代码现在应该像这样:

  private void updateMediaSessionMetaData() {
     int playState = mPlaying
            ? PlaybackStateCompat.STATE_PLAYING
            : PlaybackStateCompat.STATE_PAUSED;
           mMediaSessionCompat.setMetadata(new MediaMetadataCompat.Builder()
                .putString(MediaMetadata.METADATA_KEY_ARTIST, getArtist())
                .putString(MediaMetadata.METADATA_KEY_ALBUM, getAlbum())
                .putString(MediaMetadata.METADATA_KEY_TITLE, getSongTitle())
                .putLong(MediaMetadata.METADATA_KEY_DURATION, duration())
                .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, mSongPosn)
                .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, songs.size())
                .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, albumArt)
                .build());
mMediaSessionCompat.setPlaybackState(new PlaybackStateCompat.Builder()
                .setState(playState, position(), 1.0f)
                .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE| PlaybackStateCompat.ACTION_SKIP_TO_NEXT|PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).build()); 

更新:添加了媒体回调和接收器的代码

   private final class MediaSessionCallback extends MediaSessionCompat.Callback {

    @Override
    public void onPlay() {
        pausePlayer();
    }
    @Override
    public void onPause() {
        pausePlayer();
    }
    public void onSeekTo(long pos) {
        seek(pos);
    }
    @Override
    public void onSkipToNext() {
        playNext();
    }
    @Override
    public void onSkipToPrevious() {
        playPrev();
    }
}

接收器:

 public class MusicIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {

        if (intent.getAction().equals(
                android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {

            Intent intent1 = new Intent(MusicService.ACTION_PAUSE);
            intent1.setClass(context,
                    com.xyz.service.MusicService.class);
            // send an intent to our MusicService to telling it to pause the
            // audio
            context.startService(intent1);

        } else if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) {

            KeyEvent keyEvent = (KeyEvent) intent.getExtras().get(
                    Intent.EXTRA_KEY_EVENT);
            if (keyEvent.getAction() != KeyEvent.ACTION_DOWN)
                return;

            switch (keyEvent.getKeyCode()) {
                case KeyEvent.KEYCODE_HEADSETHOOK:
                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                    Intent intentToggle = new Intent(
                            MusicService.ACTION_TOGGLE_PLAYBACK);
                    intentToggle.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentToggle);
                    break;
                case KeyEvent.KEYCODE_MEDIA_PLAY:
                    Intent intentPlay = new Intent(MusicService.ACTION_PLAY);
                    intentPlay.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentPlay);

                    break;
                case KeyEvent.KEYCODE_MEDIA_PAUSE:
                    Intent intentPause = new Intent(MusicService.ACTION_PAUSE);
                    intentPause.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentPause);

                    break;
                case KeyEvent.KEYCODE_MEDIA_NEXT:
                    Intent intentNext = new Intent(MusicService.ACTION_NEXT);
                    intentNext.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentNext);

                    break;
                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                    Intent intentPrev = new Intent(MusicService.ACTION_PREV);
                    intentPrev.setClass(context,
                            com.xyz.service.MusicService.class);
                    context.startService(intentPrev);

                    break;
                default:
                    break;
            }
        }
    }
}

我在真实设备上进行了测试,没有使用轨道号和轨道数量,但不幸的是它再次没有起作用。 - Akshay Chordiya
它仍然有效。只需尝试我的代码,如果你遇到困难,请告诉我。 - Siju
你能把整个服务、接收器和相关代码放在Pastebin或其他地方吗?让我可以在我的设备上检查你的代码。 - Siju
试试这个。我修改了你的代码,去掉了一些不必要的东西。http://pastebin.com/Kut7JEYa - Siju
我很高兴,我会尝试一下并告诉你。 - Akshay Chordiya
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接