在《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驅動了。
MediaPlayer
之所以選擇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的接口。