在《Binder简介》一篇中,我们了解了Binder进程间通讯的大致执行原理,从这一篇开始,通过分析源码来认识Binder底层的调用过程。
Binder结构
Binder采用C/S结构的通讯方式,提供服务的为Server进程,访问的进程为Client进程,一个Server进程可以运行多个组件来为Client服务,这些组件称为Service组件。Client和Server进程都维护了自己的一个Binder线程池,因此,Client可以并发访问,Server也可以并发提供服务。每个Service组件在启动时,都会添加进ServiceManager中,它用来对组件进行统一管理。Client和Service组件由应用程序实现。ServiceManager和Binder驱动由系统实现。
/dev/binder是Binder驱动对外暴露的设备文件,Client和Server进程,通过系统调用open、mmap和ioctl来访问/dev/binder文件,进而访问Binder驱动。我们知道,在Linux中,通过操作文件的方式,便能间接控制设备,因此,操作/dev/binder文件,就相当于在操作Binder驱动了。
image.pngMediaPlayer
之所以选择MediaPlayer作为理解Binder的切入点,原因有二:一是媒体播放这种需求比较常见;二是,MediaPlayer (Client)需要通过底层MediaServer(Server)管理运行的组件MediaPlayerService,来实现上层播放功能,而MediaServer包含了许多重要的Service组件,如:
- AudioFlinger:音频系统中的核心服务。
- AudioPolicyService:音频系统中关于音频策略的重要任务。
- CameraService:摄像/照相的重要服务。
- MediaPlayerService:多媒体系统中的重要服务。
但此篇主要分析上层MediaPlayer如何通过底层,来远程关联服务端的播放器,没有涉及过多的Binder知识,若熟悉此流程的读者可以跳过。
MediaPlayer通常有2种创建方式:
MediaPlayer mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource( "http://xxxx" );
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.start();
MediaPlayer mediaPlayer = MediaPlayer.create( context, R.raw.test);
mediaPlayer.start();
无论哪种方式,其调用执行流程都是相同的,我们以第一种为例开始分析,先看MediaPlayer 构造方法。
public class MediaPlayer implements SubtitleController.Listener
{
static {
System.loadLibrary("media_jni");
native_init();
}
public MediaPlayer() {
//构建AudioAttributes
super((new Builder()).build(), 2);
Looper looper;
//myLooper不为null,构建EventHandler
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
//获取主线程Looper不为null,构建EventHandler
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
this.mTimeProvider = new MediaPlayer.TimeProvider(this);
this.mOpenSubtitleSources = new Vector();
//构建弱引用,执行native_setup
this.native_setup(new WeakReference(this));
this.baseRegisterPlayer();
}
EventHandler用于处理底层回调过来的状态,用来回调用户设置的监听器。下面再分析。我们注意到静态代码块中加载了libmedia_jni.so,并调用native_init进行了初始化,它在android_media_MediaPlayer.cpp中。
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
//获取Java的MediaPlayer类
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
//获取mNativeContext变量id,保存到fields.context
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
//获取postEventFromNative方法id,保存到fields.post_event
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
if (fields.surface_texture == NULL) {
return;
}
//获取ProxyInfo类
clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
}
fields.proxyConfigGetHost =
env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
fields.proxyConfigGetPort =
env->GetMethodID(clazz, "getPort", "()I");
fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
}
先看fields是什么。
struct fields_t {
jfieldID context;
jfieldID surface_texture;
jmethodID post_event;
jmethodID proxyConfigGetHost;
jmethodID proxyConfigGetPort;
jmethodID proxyConfigGetExclusionList;
};
static fields_t fields;
初始化的工作就是将MediaPlayer.java中定义的一些字段或方法的ID保存到fields_t 这个结构体中。这是一种通过本地去初始化Java层的类成员的方法,比如mNativeContext字段,随后底层将构造一个本地MediaPlayer对象,将这个对象的地址保存到mNativeContext变量中,也就是说,每创建一个Java类的MediaPlayer对象,也将绑定了一个Native层的MediaPlayer对象。
因此,从Native角度来说,我们平时使用的MediaPlayer.java像是底层暴露的一个接口类,实际的实现逻辑都是由底层处理的。由于Java层、Jni层和C++层用的名字都是MediaPlayer,为了描述方便,后面将用MediaPlayer.java、MediaPlayer.jni和MediaPlayer.cpp来做区分。
我们再回头看MediaPlayer.java构造方法中,被调用的native_setup方法,它将当前对象以及对象弱引用传递给底层。
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
//构件MediaPlayer.cpp对象
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// 构建JNIMediaPlayerListener对象,传入了Java层对象引用和弱引用
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
//保存到MediaPlayer.cpp对象中
mp->setListener(listener);
//将MediaPlayer.cpp对象保存到MediaPlayer.java的mNativeContext字段中
setMediaPlayer(env, thiz, mp);
}
先来看MediaPlayer.cpp对象的构建。
MediaPlayer::MediaPlayer()
{
ALOGV("constructor");
mListener = NULL;
mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;//默认音频流类型
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
mSeekPosition = -1;
mCurrentState = MEDIA_PLAYER_IDLE;//初始状态为idle空闲状态
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false; //不循环播放
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioUniqueId();
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
mRetransmitEndpointValid = false;
}
MediaPlayer.cpp构造函数只是对一些字段做了初始化,如设置默认的流类型为音频流,初始状态为空闲状态。
JNIMediaPlayerListener声明了一个notify()函数,用于底层产生错误时,通知MediaPlayer.java抛出异常的。
class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayerListener();
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
private:
JNIMediaPlayerListener();
jclass mClass; //MediaPlayer.java对象引用的全局引用
jobject mObject; //MediaPlayer.java对象弱引用的全局引用
};
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{
// 获取MediaPlayer.java
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find android/media/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
}
//创建对象引用的全局引用
mClass = (jclass)env->NewGlobalRef(clazz);
//创建对象弱引用的全局引用
mObject = env->NewGlobalRef(weak_thiz);
}
JNIMediaPlayerListener::~JNIMediaPlayerListener()
{
//释放引用
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
//调用MediaPlayer.java对象的postEventFromNative静态方法
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}
JNIMediaPlayerListener为对象的引用和弱引用创建全局引用并保存起来。如果notify函数被调用了,将回调MediaPlayer.java的postEventFromNative方法。
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
//获取弱引用
MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
if (mp == null) {
return;
}
if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
mp.start();
}
//发送消息给EventHandler
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}
}
EventHandler处理的状态比较多,我们只贴几个常见的。
private class EventHandler extends Handler
{
private MediaPlayer mMediaPlayer;
public EventHandler(MediaPlayer mp, Looper looper) {
super(looper);
mMediaPlayer = mp;
}
@Override
public void handleMessage(Message msg) {
//MediaPlayer.cpp为null
if (mMediaPlayer.mNativeContext == 0) {
return;
}
//准备成功回调
switch(msg.what) {
case MEDIA_PREPARED:
scanInternalSubtitleTracks();
if (mOnPreparedListener != null)
mOnPreparedListener.onPrepared(mMediaPlayer);
return;
//播放完成回调
case MEDIA_PLAYBACK_COMPLETE:
if (mOnCompletionListener != null)
mOnCompletionListener.onCompletion(mMediaPlayer);
stayAwake(false);
return;
//停止回调
case MEDIA_STOPPED:
if (mTimeProvider != null) {
mTimeProvider.onStopped();
}
break;
//暂停回调
case MEDIA_STARTED:
case MEDIA_PAUSED:
if (mTimeProvider != null) {
mTimeProvider.onPaused(msg.what == MEDIA_PAUSED);
}
break;
......
在MediaPlayer.jni的native_setup函数中,JNIMediaPlayerListener对象被构建后,会先保存在MediaPlayer.cpp对象中,紧接着执行setMediaPlayer函数。
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
//获取旧的
sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
//将旧的释放
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
//给fields.context设置新的MediaPlayer.cpp
env->SetLongField(thiz, fields.context, (jlong)player.get());
return old;
}
到这里,Java层的MediaPlayer.java对象便通过mNativeContext变量,绑定了底层的MediaPlayer.cpp对象。
接着看setDataSource方法。
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
_setDataSource(fd, offset, length);
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
_setDataSource的绑定使用动态注册方式实现。
static JNINativeMethod gMethods[] = {
{
"nativeSetDataSource",
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
"[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
//_setDataSource映射到android_media_MediaPlayer_setDataSourceFD
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
因此,我们看android_media_MediaPlayer_setDataSourceFD这个函数。
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
//获取MediaPlayer.cpp对象
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
//调用MediaPlayer.cpp对象的setDataSource函数
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
//获取MediaPlayer.java对象里绑定的MediaPlayer.cpp对象
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
return sp<MediaPlayer>(p);
}
调用MediaPlayer.cpp对象如果出现错误或异常,都会通过process_media_player_call来通知上层的MediaPlayer.java对象,如何通知在上面已经分析过,它会调用MediaPlayer.cpp对象的成员JNIMediaPlayerListener的notify方法。
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) {
if (opStatus != (status_t) OK) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
} else {
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
jniThrowException( env, exception, message);
} else {
char msg[256];
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
}
调用process_media_player_call函数时,调用了MediaPlayer.cpp对象的setDataSource函数。
status_t MediaPlayer::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url, const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
status_t err = BAD_VALUE;
if (url != NULL) {
//通过ServiceManager找到MediaPlayerService,初始化service对象
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
//通过Binder机制向MediaPlayerService请求创建IMediaPlayer对象
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
//调用IMediaPlayer的setDataSource
(NO_ERROR != player->setDataSource(httpService, url, headers))) {
player.clear();
}
//关联远程播放器
err = attachNewPlayer(player);
}
}
return err;
}
getMediaPlayerService便是通过Binder机制,从ServiceManger中获取到一个MediaPlayerService,并创建创建IMediaPlayer对象,这个函数在IMediaDeathNotifier.cpp中实现。
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
//获取ServiceManger
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
//获取binder
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
//将binder强转为IMediaPlayerService
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
//返回IMediaPlayerService
return sMediaPlayerService;
}
从ServiceManger中获取到Binder并将其强转成IMediaPlayerService类型。在MediaServer创建时,MediaPlayerService便注册在MediaServer中了,注册过程我们将在下一篇分析。获取到IMediaPlayerService后,紧接着调用create函数创建IMediaPlayer对象。
MediaPlayer.cpp实际上是IMediaPlayerClient类型的,即C端,显然IMediaPlayerService就是S端了。
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
int audioSessionId)
{
//每个线程只有一个IPCThreadState对象,它保存了进程ProcessState对象,负责binder的读取、写入和请求
pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId);
//创建Client
sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid());
ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid());
//转弱引用
wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
//保存到SortedVector中
mClients.add(w);
}
return c;
}
这个Client是MediaPlayerService的内部类,而且它被添加到MediaPlayerService的全局列表mClients中,这也说明了上层MediaPlayer.java可以同时有多个。
private:
class Client : public BnMediaPlayer {
// IMediaPlayer interface
virtual void disconnect();
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer);
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
virtual status_t pause();
virtual status_t isPlaying(bool* state);
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int* msec);
virtual status_t getDuration(int* msec);
virtual status_t reset();
virtual status_t setAudioStreamType(audio_stream_type_t type);
virtual status_t setLooping(int loop);
virtual status_t setVolume(float leftVolume, float rightVolume);
virtual status_t invoke(const Parcel& request, Parcel *reply);
virtual status_t setMetadataFilter(const Parcel& filter);
virtual status_t getMetadata(bool update_only,
bool apply_filter,
Parcel *reply);
virtual status_t setAuxEffectSendLevel(float level);
virtual status_t attachAuxEffect(int effectId);
virtual status_t setParameter(int key, const Parcel &request);
virtual status_t getParameter(int key, Parcel *reply);
virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint);
virtual status_t getRetransmitEndpoint(struct sockaddr_in* endpoint);
virtual status_t setNextPlayer(const sp<IMediaPlayer>& player);
我们看到,这个Client的函数定义和MediaPlayer.cpp和MediaPlayer.java是一一对应的,它的继承关系为Client->BnMediaPlayer->IMediaPlayer,也就是说,Client本质是一个IMediaPlayer,它可以创建和控制播放器。Binder通讯中会经常见到Bnxxx和Bpxxx,它们的区别以及继承关系会在下一篇分析。
再回到MediaPlayer::setDataSource函数,可以看到实际将调用IMediaPlayer的setDataSource函数。
status_t MediaPlayerService::Client::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers)
{
//路径不能为null
ALOGV("setDataSource(%s)", url);
if (url == NULL)
return UNKNOWN_ERROR;
//路径类型
if ((strncmp(url, "http://", 7) == 0) ||
(strncmp(url, "https://", 8) == 0) ||
(strncmp(url, "rtsp://", 7) == 0)) {
if (!checkPermission("android.permission.INTERNET")) {
return PERMISSION_DENIED;
}
}
//是否内容提供者提供数据
if (strncmp(url, "content://", 10) == 0) {
String16 url16(url);
int fd = android::openContentProviderFile(url16);
if (fd < 0)
{
ALOGE("Couldn't open fd for %s", url);
return UNKNOWN_ERROR;
}
setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
close(fd);
return mStatus;
} else {
//根据url获取播放器类型
player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
//创建对应的播放器
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
//对调用播放器的setDataSource
setDataSource_post(p, p->setDataSource(httpService, url, headers));
return mStatus;
}
}
安卓的播放器实际有两种,StagefrightPlayer和NuPlayer,前者是对AwesomePlayer的封装。这里的创建使用抽象工厂模式,播放器对应的工厂对象,在MediaPlayerService构造函数中初始化,维护在MediaPlayerService的成员Map中。
远程服务端的播放器通过资源路径创建完毕,MediaPlayer.cpp将通过关联远程Client(IMediaPlayer)来关联这个播放器,通过调用MediaPlayer.cpp的attachNewPlayer实现。
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
status_t err = UNKNOWN_ERROR;
sp<IMediaPlayer> p;
{
//加锁
Mutex::Autolock _l(mLock);
//出错
if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
(mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {
ALOGE("attachNewPlayer called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// Client(IMediaPlayer)保存到成员变量mPlayer 中
clear_l();
p = mPlayer;
mPlayer = player;
//状态置为MEDIA_PLAYER_INITIALIZED
if (player != 0) {
mCurrentState = MEDIA_PLAYER_INITIALIZED;
err = NO_ERROR;
} else {
ALOGE("Unable to create media player");
}
}
if (p != 0) {
p->disconnect();
}
return err;
}
到此,上层应用便和底层的播放器进行了绑定,MediaPlayer.java调用prepare和start等方法,都将通过底层的播放器去执行具体的逻辑。
这一篇只分析了播放器的创建和关联流程,其中还留下了许多疑问,并且Binder的远程调用具体过程,也还没有分析到,这些都将再下一篇进行讲解。
最后,从底层的角度出发,对各个类以及各层之间的关系做下总结:
-
MediaPlayerService是服务端,它主要功能是创建Client对象并维护了一个Client列表,以供客户端调用。
-
Client实现了IMediaPlayer定义的子类,它相当于播放器对外暴露的接口,操作Client就能操作播放器。
-
MediaPlayer.cpp是相对于MediaPlayerService的客户端,同时它相当于暴露给MediaPlayer.jni的接口,它通过Binder机制,远程调用运行在MediaPlayerService里的Client(IMediaPlayer),来操作播放器。
-
同样,MediaPlayer.jni相当于底层暴露给应用层MediaPlayer.java的接口。
网友评论