4. MediaSessionToken分发:创建与传递、客户端与服务端通信链路

4.1 原理概述

MediaSessionToken是Android多媒体框架中用于标识和验证媒体会话的唯一令牌。当Service侧创建MediaSession实例时,系统会为其生成一个全局唯一的。该通过Binder跨进程传递给客户端(如Activity或MediaBrowserService),客户端利用这个与真正的会话对象建立通信链路。整个分发机制的核心目的:确保客户端只能通过合法访问对应的媒体会话,避免无授权访问或会话劫持。

关键概念:MediaSessionToken本质上是一个Parcelable对象,内部封装了IControllerBinder接口的Binder引用。客户端拿到后,通过MediaSession.#getController()获取MediaController实例,从而发送播放命令、接收回调。

4.2 创建流程

4.2.1 Service端创建

在MediaBrowserService或自定义Service中,创建MediaSession的典型代码:

MediaSession mediaSession = new MediaSession(context, "MyMediaSession");
mediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
                      MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
// 设置回调、播放器等
mediaSession.setCallback(new MediaSession.Callback() { ... });
// 此时已创建,可通过mediaSession.getSessionToken()获取
MediaSession.  = mediaSession.getSessionToken();

系统内部流程:MediaSession构造函数会注册到MediaSessionManager,并分配一个SessionRecord。的创建时机就在此时——SessionTokenImpl持有IControllerBinder的Binder引用,并序列化自身。

4.2.2 的内部结构

字段类型说明
mPackageNameString创建Session的应用包名
mServiceNameStringSession所在Service组件名
mControllerBinderIBinder与MediaController通信的代理接口Binder
mUidint创建Session的UID
mUserHandleUserHandle用户句柄
注意:对象是Parcelable,跨进程传输时需注意序列化/反序列化性能。频繁在进程间传递大体积可能导致ANR,应只在启动时传递一次并缓存。

4.3 客户端获取的两种途径

  • 通过Binder直接返回:在MediaBrowserService#onGetRoot()返回的BrowserRoot中携带(实际上BrowserRoot内部包含)。
  • 通过MediaBrowser连接流程:客户端MediaBrowser连接成功后,通过MediaBrowser#getSessionToken()获得。

典型客户端连接代码:

// 客户端(如Activity)中
MediaBrowser mediaBrowser = new MediaBrowser(context,
    new ComponentName(this, MusicService.class),
    new MediaBrowser.ConnectionCallback() {
        @Override
        public void onConnected() {
            MediaSession.  = mediaBrowser.getSessionToken();
            MediaController controller = new MediaController(context, );
            setMediaController(controller);
        }
    }, null);
mediaBrowser.connect();

4.4 通信链路建立(Binder + )

当客户端拿到后,调用new MediaController(context, )时,内部会通过.getController()获取底层MediaControllerImplBase。该实现类利用中存储的IControllerBinder的Binder引用,直接与Service侧的MediaSessionRecord进行双向Binder通信:

  • 客户端 → 服务端:通过Binder调用sendCommand()、dispatchMediaButtonEvent()等。
  • 服务端 → 客户端:通过IControllerCallback回调播放状态、队列变化等。
链路核心:不传输播放数据,只传输控制权限。媒体数据(如音频流)通常通过另一个通道(如MediaPlayer、ExoPlayer的内部渲染器)处理,与控制链路分离。

4.5 分发SVG流程图

MediaSessionToken创建与分发流程 MediaSessionToken 创建与分发流程 Service创建MediaSession 内部生成SessionToken onGetRoot返回 客户端MediaBrowser连接 客户端获取对象 创建MediaController 双向Binder通信(IControllerBinder) 客户端←→Service SessionRecord

4.6 调试方法

  1. 日志验证是否一致:在Service端输出的hashCode(),客户端获取后比对。
  2. 使用adb命令查看Session状态:adb shell dumpsys media_session 可列出所有活跃的MediaSession及其。
  3. 检查Binder死亡通知:监听MediaControllerCompat#registerCallback()中的onSessionDestroyed(),若异常触发说明失效或Service销毁。

常见调试代码片段:

// Service侧打印
Log.d("MediaSessionDemo", " created: " + mediaSession.getSessionToken().toString());

// 客户端侧打印
MediaSession.  = mediaBrowser.getSessionToken();
Log.d("MediaSessionDemo", "Client received : " + .toString());
// 正常情况下两个toString输出应当相同(内部包含Binder的描述)

4.7 常见错误与稳定性优化

错误场景原因解决方案
客户端getSessionToken()返回nullMediaBrowser尚未完全连接就在onConnected之前调用了getSessionToken确保回调中才获取
创建MediaController时抛出IllegalStateException已被回收或Service进程已死增加重连机制,在onSessionDestroyed时重建连接
Binder通信异常(DeadObjectException)Service进程崩溃或被杀死实现MediaController.Callback#onSessionDestroyed()进行重连
多进程场景传递失败未正确实现Parcelable或Binder引用丢失确保使用官方MediaSession.,不要手动创建副本
性能优化建议:
1. 避免在频繁回调中反复通过getSessionToken()获取新实例——一旦创建应缓存重用。
2. 跨进程传递时,如果客户端长时间不使用时,及时释放对应MediaController以减少Binder引用数量。
3. 对于需要频繁交互的场景(如游戏后台播放),建议在Service侧使用MediaSessionCompat的池机制。