MediaSession 简介

开发者 2024-9-6 20:08:36 115 0 来自 中国
MediaSession 简介

本文紧张是先容下MediaSession,联合framework源码例子,末了看怎样利用MediaSession 来监听A2DP的播放活动
MediaSession 紧张是用来控制播放活动,如播放、停息等活动,不外这个控制活动是由别的一个进程来操纵的,举个例子,好比文件管理器进程在播放视频,此时你可以通过语音助手辨认语音停息、快进等下令,然后通过MediaSession将你的控制活动直接传输到文件管理器中来实现播放控制活动,就可以明白是跨进程通讯的一组接口。
API先容

Android reference doc MediaSession Android Developers
怎样实现一个MediaSession 服务端

Using a media session
Implement a media session
这里有一篇联合ExoPlayer 利用MediaSession的文档
Controlling media through MediaSession
AVRCP协媾和A2DP 的MediaSession 控制

AVRCP协议的全称是音视频远端控制协议,联合MediaSession框架可以或许很容易的实现音视频的播放控制,利用时我们只需要实现好客户端的代码,服务端由framework 中buletooth 实现。
接下来看Android Framework中的实例,来明白对MediaSession 相干利用和逻辑,这边是Android P源码,其相干的服务 A2dpMediaBrowserService.java
服务端实现

1、MediaBrowseService

起首可以看 A2dpMediaBrowserService 这个类的实现了MediaBrowserService 服务端
packages/apps/Bluetooth/src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java
摘下其解释
/** * Implements the MediaBrowserService interface to AVRCP and A2DP * * This service provides a means for external applications to access A2DP and AVRCP. * The applications are expected to use MediaBrowser (see API) and all the music * browsing/playback/metadata can be controlled via MediaBrowser and MediaController. * * The current behavior of MediaSession exposed by this service is as follows: * 1\. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is * connected and first starts playing. Before it starts playing we do not active the session. * 1.1 The session is active throughout the duration of connection. * 2\. The session is de-activated when the device disconnects. It will be connected again when (1) * happens. */public class A2dpMediaBrowserService extends MediaBrowserService {    // ...}可以先看下 MediaBrowserService 实现,其继承自 Service
/** * Base class for media browser services. * <p> * Media browser services enable applications to browse media content provided by an application * and ask the application to start playing it. They may also be used to control content that * is already playing by way of a {@link MediaSession}. * </p> * * To extend this class, you must declare the service in your manifest file with * an intent filter with the {@link #SERVICE_INTERFACE} action. * * For example: * </p><pre> * <service android:name=".MyMediaBrowserService" *          android:label="@string/service_name" > *     <intent-filter> *         <action android:name="android.media.browse.MediaBrowserService" /> *     </intent-filter> * </service> * </pre> * */public abstract class MediaBrowserService extends Service {    // ....}MediaBrowserService 抽象了 onGetRoot 和 onLoadChildren 接口出来,以是子类要实现这两个接口。
    /**     * Called to get the root information for browsing by a particular client.     * <p>     * The implementation should verify that the client package has permission     * to access browse media information before returning the root id; it     * should return null if the client is not allowed to access this     * information.     * </p>     *     * @param clientPackageName The package name of the application which is     *            requesting access to browse media.     * @param clientUid The uid of the application which is requesting access to     *            browse media.     * @param rootHints An optional bundle of service-specific arguments to send     *            to the media browser service when connecting and retrieving the     *            root id for browsing, or null if none. The contents of this     *            bundle may affect the information returned when browsing.     * @return The {@link BrowserRoot} for accessing this app's content or null.     * @see BrowserRoot#EXTRA_RECENT     * @see BrowserRoot#EXTRA_OFFLINE     * @see BrowserRoot#EXTRA_SUGGESTED     */    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,            int clientUid, @Nullable Bundle rootHints);    /**     * Called to get information about the children of a media item.     * <p>     * Implementations must call {@link Result#sendResult result.sendResult}     * with the list of children. If loading the children will be an expensive     * operation that should be performed on another thread,     * {@link Result#detach result.detach} may be called before returning from     * this function, and then {@link Result#sendResult result.sendResult}     * called when the loading is complete.     * </p><p>     * In case the media item does not have any children, call {@link Result#sendResult}     * with an empty list. When the given {@code parentId} is invalid, implementations must     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke     * {@link MediaBrowser.SubscriptionCallback#onError}.     * </p>     *     * @param parentId The id of the parent media item whose children are to be     *            queried.     * @param result The Result to send the list of children to.     */    public abstract void onLoadChildren(@NonNull String parentId,            @NonNull Result<List<MediaBrowser.MediaItem>> result);onGetRoot会在客户端发起毗连时被调用,而onLoadchildren会在客户端发起订阅哀求时被调用。onGetRoot方法的参数是clientPackageName和客户端的UID,我们可以针对这两个参数做一些限定,好比答应哪些客户端毗连之类的,假如不答应就直接返回一个null就行了,否则就返回一个新的BrowserRoot对象。函数onLoadChildren则是在客户端发起订阅哀求时被调用的,在这个函数中,我们扫描音乐文件,然后将其打包到一个list中,再返回给客户端。
回到 A2dpMediaBrowserService 服务中我们看到这两个函数的实现也是比力简单的,这个可以根据现实的业务需求来做
    @Override    public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {        return new BrowserRoot(BrowseTree.ROOT, null);    }    @Override    public synchronized void onLoadChildren(final String parentMediaId,            final Result<List<MediaItem>> result) {        if (mAvrcpCtrlSrvc == null) {            Log.w(TAG, "AVRCP not yet connected.");            result.sendResult(Collections.emptyList());            return;        }        if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);        if (!mAvrcpCtrlSrvc.getChildren(mA2dpDevice, parentMediaId, 0, 0xff)) {            result.sendResult(Collections.emptyList());            return;        }        // Since we are using this thread from a binder thread we should make sure that        // we synchronize against other such asynchronous calls.        synchronized (this) {            mParentIdToRequestMap.put(parentMediaId, result);        }        result.detach();    }2、MediaSession

着实上面我们看到的MediaBrowseService着实是封装了一层逻辑的,内里紧张的实现照旧 MediaSession,那么接下来有须要看看 MediaSession 是怎么被利用的
照旧在 A2dpMediaBrowserService 中
    @Override    public void onCreate() {        if (DBG) Log.d(TAG, "onCreate");        super.onCreate();        mSession = new MediaSession(this, TAG);        setSessionToken(mSession.getSessionToken());        mSession.setCallback(mSessionCallbacks);        mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS                | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);        mSession.setActive(true);        mAvrcpCommandQueue = new AvrcpCommandQueueHandler(Looper.getMainLooper(), this);        refreshInitialPlayingState();        IntentFilter filter = new IntentFilter();        filter.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);        filter.addAction(AvrcpControllerService.ACTION_BROWSE_CONNECTION_STATE_CHANGED);        filter.addAction(AvrcpControllerService.ACTION_TRACK_EVENT);        filter.addAction(AvrcpControllerService.ACTION_FOLDER_LIST);        registerReceiver(mBtReceiver, filter);        synchronized (this) {            mParentIdToRequestMap.clear();        }    }这里初始化之后设置了 MediaSession.Callback, 当客户端MediaController发送指令时会回调到这里
    // Media Session Stuff.    private MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() {        @Override        public void onPlay() {            if (DBG) Log.d(TAG, "onPlay");            mAvrcpCommandQueue.obtainMessage(MSG_AVRCP_PASSTHRU,                    AvrcpControllerService.PASS_THRU_CMD_ID_PLAY).sendToTarget();            // TRACK_EVENT should be fired eventually and the UI should be hence updated.        }        @Override        public void onPause() {            if (DBG) Log.d(TAG, "onPause");            mAvrcpCommandQueue.obtainMessage(MSG_AVRCP_PASSTHRU,                    AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE).sendToTarget();            // TRACK_EVENT should be fired eventually and the UI should be hence updated.        }        // ....    }关于MediaSession 内部的实现可以看这篇文章
Android MediaSession简单分析 - 简书
客户端调用

1、MediaBrowser + MediaController

MediaBrowser 媒体欣赏器,用来毗连媒体服务MediaBrowserService和订阅数据,在注册的回调接口中我们就可以获取到Service的毗连状态、获取音乐数据。一样平常在客户端中创建
MediaController 媒体控制器,在客户端中工作,通过控制器向媒体服务器发送指令,然后通过MediaControllerCompat.Callback设置回调函数来继承服务端的状态。MediaController创建时需要受控端的配对令牌,因此需要在欣赏器毗连乐成后才举行
以是要监听哪个服务端需要在MediaBrowser毗连服务的地方转达包名和类名, 这里利用了 MediaSessionCompat
mBrowser = new MediaBrowserCompat(MainActivity.this, new ComponentName(packageName,className), connectionCallback,null);// 而且注册MediaControler connect callback,假如毗连乐成则回调这个是注册mediacontroller的回调,
mController = new MediaControllerCompat(MainActivity.this, mBrowser.getSessionToken());mController.registerCallback(controllerCallback);将上面的packageName和className改成avrcp协议对应的服务就行了,但是差别的android版本对应的协议包名类名不一样 android7-9
String package = "com.android.bluetooth";String class = "com.android.bluetooth.a2dpsink.mbs.A2dpMediaBrowserService"android10以后
String package = "com.android.bluetooth";String class = "com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService"2、MediaSessionManager + MediaController

通过SessionManager获取全部激活的session,然后编译此中获取你想要的controller
        mMediaCtrlCallback = new MediaControllerCallback();        mSessionManager =                (MediaSessionManager) getSystemService(MEDIA_SESSION_SERVICE);        mSessionListener = new SessionChangeListener();        if (mSessionManager != null) {            mSessionManager.addOnActiveSessionsChangedListener(mSessionListener, null,                    mHandler);            List<MediaController> controllers = mSessionManager.getActiveSessions(null);            for (int i = 0; i < controllers.size(); i++) {                MediaController controller = (MediaController) controllers.get(i);                if ((getMediaControllerTag(controller).contains(A2DP_MBS_TAG))) {                    setCurrentMediaController(controller);                }            }        }需要上述代码中TAG名需要对上,这里看的源码是Android P,AVRCP服务端中注册的TAG是
A2dpMediaBrowserService
看MediaController 中的方法
    /**     * Get the session owner's package name.     *     * @return The package name of of the session owner.     */    public String getPackageName() {        if (mPackageName == null) {            try {                mPackageName = mSessionBinder.getPackageName();            } catch (RemoteException e) {                Log.d(TAG, "Dead object in getPackageName.", e);            }        }        return mPackageName;    }    /**     * Get the session's tag for debugging purposes.     *     * @return The session's tag.     * @hide     */    public String getTag() {        if (mTag == null) {            try {                mTag = mSessionBinder.getTag();            } catch (RemoteException e) {                Log.d(TAG, "Dead object in getTag.", e);            }        }        return mTag;    }然后再向对应的controller 注册 callback来利用即可
    mMediaController.registerCallback(mMediaCtrlCallback);mMediaCtrlCallback 实现抽象接口类如下,可以监听到播放状态和多媒体信息变革的修改
    private class MediaControllerCallback extends MediaController.Callback {        @Override        public void onPlaybackStateChanged(PlaybackState state) {        }        @Override        public void onMetadataChanged(MediaMetadata metadata) {        }    }
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-12-4 16:54, Processed in 0.168502 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表