Android Media Player回调事件传递

原创
2012/06/28 11:27
阅读数 6.7K
Android Media Player回调事件传递
--- 比如:节目播放完成事件如何回馈到Java应用空间
通过分析回调事件的传递,有助于进一步理解Android Media Player 框架。

(1) setOnCompletionListener(): 应用程序注册回调对象

[MediaPlayer.java]

----------------------------------------------------------------------------------
    public interface OnCompletionListener
    {
        void onCompletion(MediaPlayer mp);
    }

    public void setOnCompletionListener(OnCompletionListener listener)
    {
        mOnCompletionListener = listener;
    }

    private OnCompletionListener mOnCompletionListener;
----------------------------------------------------------------------------------
应用程序调用setOnCompletionListener()注册回调对象,该对象的onCompletion()方法在MediaPlayer.EventHandler.handleMessage()中被调用。
----------------------------------------------------------------------------------
            case MEDIA_PLAYBACK_COMPLETE:
                if (mOnCompletionListener != null)
                    mOnCompletionListener.onCompletion(mMediaPlayer);
                return;
----------------------------------------------------------------------------------

(2) postEventFromNative(): 传递来自Native的事件
[MediaPlayer.java]
----------------------------------------------------------------------------------
    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 (mp.mEventHandler != null) {
            Message m = mp.mEventHandler.obtainMessage(what, arg1,

arg2, obj);
            mp.mEventHandler.sendMessage(m);
        }
    }
----------------------------------------------------------------------------------
postEventFromNative()是在哪里被调用的呢?应该是Native世界。

(3) env->CallStaticVoidMethod(): 调用postEventFromNative()
[android_media_MediaPlayer.cpp]
I. android_media_MediaPlayer_native_init(): 记录"postEventFromNative()"方法的ID
-----------------------------------------------------------------------------------
android_media_MediaPlayer_native_init(JNIEnv *env)
{
    clazz = env->FindClass("android/media/MediaPlayer");

    fields.post_event = env->GetStaticMethodID(clazz,
"postEventFromNative",                    "(Ljava/lang/Object;IIILjava/lang/Object;)

V");
}
-----------------------------------------------------------------------------------
该native方法在MediaPlayer.java类第一次被加载时调用。
-----------------------------------------------------------------------------------
[MediaPlayer.java]
public class MediaPlayer
{
    ... ...
    static {
        System.loadLibrary("media_jni");
        native_init();
    }
    ... ...
}
-----------------------------------------------------------------------------------

II. android_media_MediaPlayer_native_setup(): 创建Native中的MediaPlayer客户端
该native方法在构造MediaPlayer.java对象时被调用,
-----------------------------------------------------------------------------------
[MediaPlayer.java]
public class MediaPlayer
{
    ... ...
    public MediaPlayer() {
        ... ...
        native_setup(new WeakReference<MediaPlayer>(this));
    }
    ... ...
}
-----------------------------------------------------------------------------------
该方法创建Native的MediaPlayer,
-----------------------------------------------------------------------------------
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz,

jobject weak_this)
{
    sp<MediaPlayer> mp = new MediaPlayer();

    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener

(env, thiz, weak_this);
    mp->setListener(listener);

    setMediaPlayer(env, thiz, mp);
}
-----------------------------------------------------------------------------------
也就是说MediaPlayer.java构造时,创建了一个Native的MediaPlayer,
该MediaPlayer继承BnMediaPlayerClient,所以可称为 --- 提供Binder服
务的MediaPlayer客户端。也就是说,它本身是MediaPlayer客户端,同
时它也提供Binder服务,估计目的就是使MediaPlayer服务端能回调客户
端的方法。简称Native中的MediaPlayer客户端。
该方法同时设置了listener,该listener的notify()方法会调用
postEventFromNative()。

-----------------------------------------------------------------------------------
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const

Parcel *obj)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                msg, ext1, ext2, NULL);
}
-----------------------------------------------------------------------------------

III. MediaPlayer::notify(): Native中的MediaPlayer客户端notify()方法
-----------------------------------------------------------------------------------
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    ... ...
   mListener->notify(msg, ext1, ext2, obj);
}
-----------------------------------------------------------------------------------
所以MediaPlayer::notify()何时被调用?
应该是被BpMediaPlayerClient调用,怎么找到它?

IV. MediaPlayer::setDataSource():传出Native中MediaPlayer客户端的代理
-----------------------------------------------------------------------------------
status_t MediaPlayer::setDataSource(
        const char *url, const KeyedVector<String8, String8> *headers)
{
        const sp<IMediaPlayerService>& service(getMediaPlayerService());
        if (service != 0) {
            sp<IMediaPlayer> player(service->create(getpid(), this,

mAudioSessionId));
            if (NO_ERROR != player->setDataSource(url, headers)) {
                player.clear();
            }
            err = attachNewPlayer(player);
        }
    return err;
}
-----------------------------------------------------------------------------------
这里取得IMediaPlayer的代理端,同时也把IMediaPlayerClient的服务端即BnMediaPlayerClient传递出去。

总结一:
MediaPlayer.java对象构造时,创建Native中的MediaPlayer客户端。
setDataSource()方法会取得IMediaPlayer代理端。来自IMediaPlayer的回调通过BpMediaPlayerClient调用到Java空间。

V. BpMediaPlayerClient.notify()被调用
BnMediaPlayerClient通过上面的service->create()传递,该create最后调用(通过binder),
-----------------------------------------------------------------------------------
sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const

sp<IMediaPlayerClient>& client,
        int audioSessionId)
{
    sp<Client> c = new Client(
            this, pid, connId, client, audioSessionId,
            IPCThreadState::self()->getCallingUid());
   ... ...
    return c;
}
-----------------------------------------------------------------------------------
Client的构造函数,记录client到mClient中。client的notify()方法被MediaPlayerService::Client::notify()调用。

VI.  setDataSource() 最终创建MeidaPlayerBase
-----------------------------------------------------------------------------------
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer

(player_type playerType)
{
    sp<MediaPlayerBase> p = mPlayer;
    if (p == NULL) {
        p = android::createPlayer(playerType, this, notify);
    }

    return p;
}

static sp<MediaPlayerBase> createPlayer(player_type playerType, void*

cookie,
        notify_callback_f notifyFunc)
{
    sp<MediaPlayerBase> p;
    switch (playerType) {
        case STAGEFRIGHT_PLAYER:
            LOGV(" create StagefrightPlayer");
            p = new StagefrightPlayer;
            break;
        case NU_PLAYER:
            LOGV(" create NuPlayer");
            p = new NuPlayerDriver;
            break;
        default:
            LOGE("Unknown player type: %d", playerType);
            return NULL;
    }
    if (p != NULL) {
        if (p->initCheck() == NO_ERROR) {
            p->setNotifyCallback(cookie, notifyFunc);
        } else {
            p.clear();
        }
    }
    return p;
}
-----------------------------------------------------------------------------------

VII. StagefrightPlayer->setNotifyCallback()
记录notifyFunc到mNotify中

VIII. StageFrightPlayer->sendEvent()
最终连锁触发到postEventFromNative()

[base\media\libstagefright\awesomePlayer.cpp]
void AwesomePlayer::onStreamDone() {
        ... ...
        notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
}

void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
    if (mListener != NULL) {
        sp<MediaPlayerBase> listener = mListener.promote();

        if (listener != NULL) {
            listener->sendEvent(msg, ext1, ext2);
        }
    }
}

这样整个旧贯通了。

再总结:
1) MediaPlayer.java对象构造,创建Native中的MediaPlayer客户端;
setDataSource()方法会取得IMediaPlayer代理端, 即创建了IMediaPlayer的服务端,进而创建如StageFrightPlayer。
回调通过StageFrightPlayer引用BpMediaPlayerClient送给BnMediaPlayerClient,即Native中的MediaPlayer客户端,然后调回java空间。

可惜图片发不了!

展开阅读全文
打赏
0
1 收藏
分享
加载中
学习一下android回调。
2013/04/12 13:59
回复
举报
更多评论
打赏
1 评论
1 收藏
0
分享
返回顶部
顶部