博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Media Player回调事件传递
阅读量:6860 次
发布时间:2019-06-26

本文共 7591 字,大约阅读时间需要 25 分钟。

hot3.png

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空间。
可惜图片发不了!

转载于:https://my.oschina.net/u/580871/blog/64154

你可能感兴趣的文章
混合云?行业云?“合脚”的才是好用的云
查看>>
掘金全球最大商务差旅市场 SAP旗下Concur联合中数通进军中国
查看>>
大变样:Firefox新一代UI “Photon”设计曝光
查看>>
云计算、IoT和SDN为企业网带来最大的问题
查看>>
云趋势下的Windows平台:生存并快乐着
查看>>
不要再在JavaScript中写 CSS了
查看>>
Gartner:云安全进入高速发展期
查看>>
云存储能否成为数据安全灵药?几个角度全方位剖析
查看>>
未来几年SDN将进一步提升云服务利润率
查看>>
手把手教你用 Python 和 Scikit-Learn 实现垃圾邮件过滤
查看>>
Hinton亲自讲解迄今未发表工作:胶囊理论的核心概念到底是什么?
查看>>
光伏业需要一次国内“双反”
查看>>
小微企业都在用的一体化管理解决方案
查看>>
Sql Server 2008 为开发带来的新特性
查看>>
Realm为Node.js发布对象数据库
查看>>
物联网行业将掀起新一轮并购潮 步入整合期
查看>>
夏日炎炎 构筑安防线 这些知识你Get到了吗?
查看>>
《C语言程序设计:问题与求解方法》——2.4节C语言源程序的次要组成成分:编译预处理命令、注释和声明...
查看>>
业绩不佳引裁员 雅虎2016有点烦
查看>>
《Hadoop实战第2版》——导读
查看>>