很苦恼没有详细的资料介绍MediaSession,也没有多少现成的例子demo可以参考。那就自己研究一下源码。
先看一下先相关的类,MediaSession、MediaController,SessionToken,MediaSessionService。
MediaSession构造函数
public MediaSession(@NonNull Context context, @NonNull String tag, int userId) {
if (context == null) {
throw new IllegalArgumentException("context cannot be null.");
}
if (TextUtils.isEmpty(tag)) {
throw new IllegalArgumentException("tag cannot be null or empty");
}
mMaxBitmapSize = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
mCbStub = new CallbackStub(this); //第一处
MediaSessionManager manager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
mBinder = manager.createSession(mCbStub, tag, userId); //第二处
mSessionToken = new Token(mBinder.getController()); //第三处
mController = new MediaController(context, mSessionToken); //第四处
} catch (RemoteException e) {
throw new RuntimeException("Remote error creating session.", e);
}
}
先看第一处,CallbackStub,看几行就大概明白意思
public static class CallbackStub extends ISessionCallback.Stub {
private WeakReference<MediaSession> mMediaSession;
public CallbackStub(MediaSession session) {
mMediaSession = new WeakReference<MediaSession>(session);
}
@Override
public void onCommand(String command, Bundle args, ResultReceiver cb) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.postCommand(command, args, cb);
}
}
@Override
public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber,
ResultReceiver cb) {
MediaSession session = mMediaSession.get();
try {
if (session != null) {
session.dispatchMediaButton(mediaButtonIntent);
}
} finally {
if (cb != null) {
cb.send(sequenceNumber, null);
}
}
}
@Override
public void onPrepare() {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrepare();
}
}
就是将自己封装一下,传给别的类,以便让别的类调用这些功能。也就是说,具体的控制功能是通过这些回调实现的。
这些功能是怎么实现的?MediaSession还有个内部类Callback,需要开发者具体实现它。比如
public void onPrepare() {}
public void onPrepareFromMediaId(String mediaId, Bundle extras) { }
public void onPrepareFromSearch(String query, Bundle extras) { }
具体的调用关系,已prepare为例,
public void onPrepare() {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrepare();
}
}
private void dispatchPrepare() {
postToCallback(CallbackMessageHandler.MSG_PREPARE);
}
case MSG_PREPARE:
mCallback.onPrepare();
这个mCallback就是MediaSession.Callback类型,即需要实现的。
第二处,
mBinder = manager.createSession(mCbStub, tag, userId)
最终会调用到MediaSessionService的
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
String callerPackageName, ISessionCallback cb, String tag) {
final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
callerPackageName, cb, tag, this, mHandler);
try {
cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
mAllSessions.add(session);
mPriorityStack.addSession(session);
UserRecord user = getOrCreateUser(userId);
user.addSessionLocked(session);
mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
if (DEBUG) {
Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
}
return session;
}
创建了一个MediaSessionRecord对象,并把它放到mAllSessions统一管理。注意,ISessionCallback对象也传过来了。
mAllSessions 对象是ArrayList<MediaSessionRecord>类型。在这个服务中,SessionManagerImpl才是真正的Manager对应的service,因为它实现了ISessionManager.Stub。
所以
mBinder = manager.createSession(mCbStub, tag, userId)
mBinder指向的就是一个ISession对象,通过MediaSessionRecord.getSessionBinder() 获得。
第三处,创建Token。
mSessionToken = new Token(mBinder.getController());
token,即标识,身份的代表吧。每个token与每个session一一对应。
又引入几个对象,SessionStub,SessionCb,ControllerStub。
前面提到,mBinder指向的是ISession对象,具体实现类是SessionStub。
所以mBinder.getController()对应着SessionStub.getController(),为ISessionController对象,具体实现为ControllerStub类。
还有个SessionCb类,就是封装(组合)了MediaSession对象的内部类ISessionCallback。
第四处,创建MediaController
mController = new MediaController(context, mSessionToken);
public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
this(context, token.getBinder());
}
此类的介绍
Allows an app to interact with an ongoing media session. Media buttons and other commands can be sent to the session. A callback may be registered to receive updates from the session, such as metadata and play state changes.
A MediaController can be created through MediaSessionManager
if you hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an enabled notification listener or by getting a MediaSession.Token
directly from the session owner.
它的构造函数
public MediaController(Context context, ISessionController sessionBinder) {
if (sessionBinder == null) {
throw new IllegalArgumentException("Session token cannot be null");
}
if (context == null) {
throw new IllegalArgumentException("Context cannot be null");
}
mSessionBinder = sessionBinder;
mTransportControls = new TransportControls();
mToken = new MediaSession.Token(sessionBinder);
mContext = context;
}
mSessionBinder对象,对应的就是Token类的mBinder,也就是MediaSessionRecord类的ControllerStub。
随便浏览MediaController的几个函数,基本上都是通过mSessionBinder的实现。
这时候,我们基本上可以做一个大概的猜测,
1. 客户端使用MediaSession类做播放器(并实现MediaSession.Callback供控制端调用);
这个作为受控端
2. 控制端
可以是任意app,通过调用服务查询session,然后就可以控制session的操作。并向session注册自己的callback,以便session状态改变时通知自己。
这篇博客总结的不错,可以参考 http://blog.csdn.net/belyxiong/article/details/42039665