android 多媒體播放 MediaSession 框架


MediaSession 框架是 Google 推出專門解決媒體播放時界面和服務通訊問題。這個框架可以讓我們不再使用廣播來控制播放器,而且也能適配耳機,藍牙等一些其它設備,實現線控的功能

要理解MediaSession框架,分別看看Media和Session:首先Media是媒體的意思,也就是說這個框架用於音視頻媒體;而Session呢,翻譯成中文就是會話的意思。一個會話,肯定是涉及兩方或以上;在MediaSession框架中,有受控端(一個)和控制端(可以有多個)。接下來爲了保證受控端和控制端不串號(想象一個遙控器可以遙控同一型號的多臺電視),就有了SessionToken的概念,相當於我們在連接藍牙設備時的配對碼,這樣就保證了不串號。在MediaSession框架中,最重要的三個類的概念就這麼多,


核心類

  • MediaSessionCompat
  • PlaybackStateCompat
  • MediaMetadataCompat
  • MediaSessionCompat.Callback

初始化配置

    /**
     * 初始化並激活 MediaSession
     */
    private void setupMediaSession() {
//        第二個參數 tag: 這個是用於調試用的,隨便填寫即可
        mMediaSession = new MediaSessionCompat(context, TAG);
        //指明支持的按鍵信息類型
        mMediaSession.setFlags(
                MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
        );
        mMediaSession.setCallback(callback);
        mMediaSession.setActive(true);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

指定可以接收的來自鎖屏頁面的按鍵信息

    private static final long MEDIA_SESSION_ACTIONS =
            PlaybackStateCompat.ACTION_PLAY
                    | PlaybackStateCompat.ACTION_PAUSE
                    | PlaybackStateCompat.ACTION_PLAY_PAUSE
                    | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
                    | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
                    | PlaybackStateCompat.ACTION_STOP
                    | PlaybackStateCompat.ACTION_SEEK_TO;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.0之後耳機物理按鍵的回調監聽

   /**
     * API 21 以上 耳機多媒體按鈕監聽 MediaSessionCompat.Callback
     */
    private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {

//        接收到監聽事件,可以有選擇的進行重寫相關方法

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

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

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

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

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

        @Override
        public void onSeekTo(long pos) {
            super.onSeekTo(pos);
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

配置完成後,播放狀態,及音樂信息的更新

    /**
     * 更新播放狀態,播放/暫停/拖動進度條時調用
     */
    public void updatePlaybackState() {
        int state = isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
        mMediaSession.setPlaybackState(
                new PlaybackStateCompat.Builder()
                        .setActions(MEDIA_SESSION_ACTIONS)
                        .setState(state, getCurrentPosition(), 1)
                        .build());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
    /**
     * 更新正在播放的音樂信息,切換歌曲時調用
     */
    public void updateMetaData(String path) {
        if (!StringUtils.isReal(path)) {
            mMediaSession.setMetadata(null);
            return;
        }

        SongInfo info = mediaManager.getSongInfo(context, path);
        MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, info.getTitle())
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, info.getArtist())
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, info.getAlbum())
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, info.getArtist())
                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, info.getDuration())
                .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getCoverBitmap(info));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getCount());
        }

        mMediaSession.setMetadata(metaData.build());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

完整代碼

package com.zdd.musicplayer.service;

import android.content.Context;
import android.drm.DrmStore;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.RemoteException;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;

import com.zdd.musicplayer.R;
import com.zdd.musicplayer.aidl.IPlayControl;
import com.zdd.musicplayer.manager.MediaManager;
import com.zdd.musicplayer.modle.SongInfo;
import com.zdd.musicplayer.util.StringUtils;

import java.util.zip.Inflater;

/**
 * Project: MusicPlayer
 * Created by Zdd on 2018/2/2.
 * <p>
 * 耳機線控
 * <p>
 * Android 5.0中新增了MediaSession類專門解決媒體播放時界面和服務通訊問題,官方說明是  允許與媒體控制器、音量鍵、媒體按鈕和傳輸控件交互。
 * 包含了媒體控制和線控等功能
 * 這個框架可以讓我們不再使用廣播來控制播放器,而且也能適配耳機,藍牙等一些其它設備,實現線控的功能。
 * <p>
 * 但是對於低版本上的仍需要自己控制
 * <p>
 * 參考文章 http://www.jianshu.com/p/bc2f779a5400;
 */

public class MediaSessionManager {

    private static final String TAG = "MediaSessionManager";

    //指定可以接收的來自鎖屏頁面的按鍵信息
    private static final long MEDIA_SESSION_ACTIONS =
            PlaybackStateCompat.ACTION_PLAY
                    | PlaybackStateCompat.ACTION_PAUSE
                    | PlaybackStateCompat.ACTION_PLAY_PAUSE
                    | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
                    | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
                    | PlaybackStateCompat.ACTION_STOP
                    | PlaybackStateCompat.ACTION_SEEK_TO;


    private final IPlayControl control;
    private final Context context;
    private MediaSessionCompat mMediaSession;
    private final MediaManager mediaManager;

    public MediaSessionManager(IPlayControl control, Context context) {
        this.control = control;
        this.context = context;
        this.mediaManager = MediaManager.getInstance();
        setupMediaSession();
    }

    /**
     * 初始化並激活 MediaSession
     */
    private void setupMediaSession() {
//        第二個參數 tag: 這個是用於調試用的,隨便填寫即可
        mMediaSession = new MediaSessionCompat(context, TAG);
        //指明支持的按鍵信息類型
        mMediaSession.setFlags(
                MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
        );
        mMediaSession.setCallback(callback);
        mMediaSession.setActive(true);
    }

    /**
     * 更新播放狀態, 播放/暫停/拖動進度條時調用
     */
    public void updatePlaybackState() {
        int state = isPlaying() ? PlaybackStateCompat.STATE_PLAYING :
                PlaybackStateCompat.STATE_PAUSED;

        mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                .setActions(MEDIA_SESSION_ACTIONS)
                .setState(state, getCurrentPosition(), 1)
                .build());
    }

    private long getCurrentPosition() {
        try {
            return control.getProgress();
        } catch (RemoteException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 是否在播放
     *
     * @return
     */
    protected boolean isPlaying() {
        try {
            return control.status() == PlayController.STATUS_PLAYING;
        } catch (RemoteException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 更新正在播放的音樂信息,切換歌曲時調用
     */
    public void updateMetaData(String path) {
        if (!StringUtils.isReal(path)) {
            mMediaSession.setMetadata(null);
            return;
        }
        SongInfo songInfo = mediaManager.getSongInfo(context, path);

        MediaMetadataCompat.Builder metaDta = new MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, songInfo.getTitle())
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, songInfo.getArtist())
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, songInfo.getAlbum())
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, songInfo.getArtist())
                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, songInfo.getDuration())
                .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getCoverBitmap(songInfo));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            metaDta.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getCount());
        }
        mMediaSession.setMetadata(metaDta.build());

    }

    private long getCount() {
        try {
            return control.getPlayList().size();
        } catch (RemoteException e) {
            e.printStackTrace();
            return 0;
        }
    }

    private Bitmap getCoverBitmap(SongInfo info) {
        if (StringUtils.isReal(info.getAlbum_path())) {
            return BitmapFactory.decodeFile(info.getAlbum_path());
        } else {
            return BitmapFactory.decodeResource(context.getResources(), R.drawable.default_song);
        }
    }


    /**
     * 釋放MediaSession,退出播放器時調用
     */
    public void release() {
        mMediaSession.setCallback(null);
        mMediaSession.setActive(false);
        mMediaSession.release();
    }


    /**
     * API 21 以上 耳機多媒體按鈕監聽 MediaSessionCompat.Callback
     */
    private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {

//        接收到監聽事件,可以有選擇的進行重寫相關方法

        @Override
        public void onPlay() {
            try {
                control.resume();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onPause() {
            try {
                control.pause();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSkipToNext() {
            try {
                control.next();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSkipToPrevious() {
            try {
                control.pre();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onStop() {
            try {
                control.pause();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSeekTo(long pos) {
            try {
                control.seekTo((int) pos);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章