1 TimedEventQueue

来源:互联网 发布:模架编程 编辑:程序博客网 时间:2024/05/03 01:27

目录

1、概述

2、一个典型的使用TimedEventQueue的例子

3、TimedEventQueue类介绍

 

1概述


    TimeEventQueue是android多媒体系统中实现异步操作的一种手段

    在Awesomeplayer中,依赖mVideoEvent、mStreamDoneEvent、mBufferingEvent、mCheckAudioStatusEvent等几个事件驱动着整个播放流程

    可以认为TimeEventQueue 是一个调度系统,调度的对象是事件Event,一个TimeEventQueue 可以同时处理多个事件

    本篇只分析TimeEventQueue 类结构及其用法

 

2、一个典型的使用TimedEventQueue的例子


一个简单的使用TimedEventQueue 的序列如下

mEventRead = new dtEvent(this,&dtPlayer::onReadEvent);             mQueue.start();             mQueue.postEvent(mEventRead);             mQueue.stop();

【说明】

首先建立一个事件

启动TimedEventQueue

通过postEvent触发事件,事件触发后会执行mEventRead对应的fire函数

最后结束

 

我们首先建立一个事件类

structdtEvent : publicTimedEventQueue::Event {                  dtEvent(dtPlayer *player,void(dtPlayer::*method)()):                     mPlayer(player),                    mMethod(method)                   {                  }                protected:                  virtual~dtEvent() {}                    virtualvoid fire(TimedEventQueue *queue, int64_t/* now_us */) {                      (mPlayer->*mMethod)();                  }                private:                  dtPlayer *mPlayer;                  void(dtPlayer::*mMethod)();                    dtEvent(constdtEvent &);                  dtEvent &operator=(constdtEvent &);              };


这里处理方法与awesomeplayer等类似,由于Event是抽象类,不能直接使用,因此需要建立一个继承自Event的子类,并重载fire方法

这里构造函数中传入了dtPlayer::*method 参数,作为fire中执行的函数体。这样就保证了对于每一个事件,通过传入不同的mMethod方法,便可以做出不同的反应

 

类似Awesomeplayer,我们需要为TimedEventQueue建立一个载体类dtPlayer

class dtPlayer{           public:                 dtPlayer();                 virtual ~dtPlayer(){};             private:               TimedEventQueue mQueue;               bool mQueueStarted;                 sp<TimedEventQueue::Event> mEventRead;                 sp<TimedEventQueue::Event> mEventWrite;                 sp<TimedEventQueue::Event> mEventSearch;                   voidonReadEvent();               voidonWriteEvent();               voidonSearchEvent();           public:               voidstart();               voidstop();               bool isplaying();               voidpostRandomEvent();           private:               dtPlayer(constdtPlayer &);               dtPlayer &operator=(constdtPlayer &);           };


 在载体类中,我们有

TimedEventQueue 对象mQueue,

三个Event对象,分别是mEventRead mEventWrite mEventSearch

三个方法,分别是传入给上面三个事件的参数,是void onReadEvent(); void onWriteEvent(); void onSearchEvent();

状态机:start stop 等

postRandomEvent 方法:封装的触发事件的方法

 

下面看下具体实现

dtPlayer::dtPlayer()          {            mEventRead =new dtEvent(this,&dtPlayer::onReadEvent);              mEventWrite =new dtEvent(this,&dtPlayer::onWriteEvent);              mEventSearch =new dtEvent(this,&dtPlayer::onSearchEvent);               mQueueStarted=false;           }

先看下构造函数,构造函数主要是生成三个事件,并将参数传递进去,这里可以看到调用的是重载的event的构造函数

并设置mQueue状态

void dtPlayer::onReadEvent()          {            ALOGI("--read event occur-- read something \n"); }            void dtPlayer::onWriteEvent()          {            ALOGI("--write event occur-- write something \n"); }            void dtPlayer::onSearchEvent()          {            ALOGI("--search event occur-- search something \n");             }


 对应的实现,只加了打印,表示执行过了

void dtPlayer::start()        {          if(mQueueStarted==false)            mQueue.start();          mQueueStarted=true;          printf("dtplayer start  \n");        }          void dtPlayer::stop()        {          if(mQueueStarted==true)            mQueue.stop(true);          mQueueStarted=false;          }          bool dtPlayer::isplaying()        {          return(mQueueStarted==true);        }

状态机代码,主要是控制mQueue的状态

void dtPlayer::postRandomEvent()        {          mQueue.postEvent(mEventRead);        }

事件触发函数,这里可以看到触发方法便是调用mQueue的postEvent方法,并且将事件对象作为参数传入。这样便可以执行对应事件的fire方法了

 

extern "C"int dt_test()        {            ALOGE("--post event start \n");            dtPlayer *player=newdtPlayer();            player->start();            player->postRandomEvent();            player->stop();            ALOGE("--post event end\n");            sleep(5);            return0;        }

这里实现了测试code,只是简单的触发一个事件然后返回

【说明】具体的例子源代码已经打包放在了附件中

测试方法:

(1)将压缩包拷贝至framework/av/media/libstagefright目录下

(2)解压缩并进入_timeeventqueue_test文件夹

(3)mm

(4)将可执行文件拷贝到目标板执行即可

 

通过上面的例子可以看到,TimedEventQueue 的使用时比较简单的,只要新建一个Event子类,并重载fire方法

之后通过TimedEventQueue 的postEvent等就可以出发执行了

而且这里一个TimedEventQueue 可以支持多个事件

 

下面看下TimedEventQueue 具体是如何实现这一机制的

 

3、TimedEventQueue类介绍


2.1 event事件

TimedEventQueue调度的对象是事件

先看下事件的定义,事件是定义在TimedEventQueue 结构体内部的结构体

 typedef int32_t event_id;    struct Event : public RefBase {        Event()            : mEventID(0) {        }        virtual ~Event() {}        event_id eventID() {            return mEventID;        }    protected:        virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;    private:        friend class TimedEventQueue;        event_id mEventID;        void setEventID(event_id id) {            mEventID = id;        }        Event(const Event &);        Event &operator=(const Event &);    };


说明:

mEventID 事件ID

fire 纯虚函数,事件触发时调度执行的函数

因此Event不能直接使用,需继承并重载fire方法后使用

 

 2.2 TimedEventQueue 一些重要方法解析

挑一下比较重要的解释下

void TimedEventQueue::start() {            if(mRunning) {                return;            }            mStopped =false;            pthread_attr_t attr;            pthread_attr_init(&attr);            pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);            pthread_create(&mThread, &attr, ThreadWrapper,this);            pthread_attr_destroy(&attr);            mRunning =true;        }


这里调用mQueue->start 后主要是启动一个线程,看下线程代码

// static        void *TimedEventQueue::ThreadWrapper(void *me) {              androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);              static_cast<TimedEventQueue *>(me)->threadEntry();              returnNULL;        }

 这里线程实现方法的主要作用是调用mQueue->threadEntry 方法,因为上面传入的参数是this指针

代码比较多我们分段来看

void TimedEventQueue::threadEntry() {           prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue",0, 0,0);             for(;;) {               int64_t now_us =0;               sp<Event> event;                 {                   Mutex::Autolock autoLock(mLock);                     if(mStopped) {                       break;                   }                     while(mQueue.empty()) {                       mQueueNotEmptyCondition.wait(mLock);                   }

首先判断mStopped,确定是否要退出

然后判断mQueue中是否为空,若未空表示没有要处理的事件,则等待

event_id eventID = 0;                  for(;;) {                      if(mQueue.empty()) {                          // The only event in the queue could have been cancelled                          // while we were waiting for its scheduled time.                          break;                      }                        List<QueueItem>::iterator it = mQueue.begin();                      eventID = (*it).event->eventID();                        now_us = ALooper::GetNowUs();                      int64_t when_us = (*it).realtime_us;                        int64_t delay_us;                      if(when_us < 0 || when_us == INT64_MAX) {                          delay_us = 0;                      }else {                          delay_us = when_us - now_us;                      }                        if(delay_us <= 0) {                          break;                      }                        staticint64_t kMaxTimeoutUs = 10000000ll;  // 10 secs                      booltimeoutCapped = false;                      if(delay_us > kMaxTimeoutUs) {                          ALOGW("delay_us exceeds max timeout: %lld us", delay_us);                            // We'll never block for more than 10 secs, instead                          // we will split up the full timeout into chunks of                          // 10 secs at a time. This will also avoid overflow                          // when converting from us to ns.                          delay_us = kMaxTimeoutUs;                          timeoutCapped =true;                      }                        status_t err = mQueueHeadChangedCondition.waitRelative(                              mLock, delay_us * 1000ll);                        if(!timeoutCapped && err == -ETIMEDOUT) {                          // We finally hit the time this event is supposed to                          // trigger.                          now_us = ALooper::GetNowUs();                          break;                      }                  }


若mQueue事件列表非空,则有要处理的事件,进入for loop进行处理,主要工作有

从mQueue的event列表中获取一个event

判断是否执行,如果时间到了(delay_us <= 0),则跳出循环执行事件处理函数,否则若需等待时间低于10s,则mQueueHeadChangedCondition.waitRelative等待。若高于10s,等10s后重新轮询

当条件满足时,通过event = removeEventFromQueue_l(eventID);获取事件对象,并调用event->fire(this, now_us);

 

这里有几个问题

(1)对mQueue,每个event都有一个唯一eventID,mQueue通过eventID来获取事件对象,eventID如何计算的

此问题的答案在mQueue->postEvent中。看下代码

TimedEventQueue::event_id TimedEventQueue::postEvent(constsp<Event> &event) {       // Reserve an earlier timeslot an INT64_MIN to be able to post       // the StopEvent to the absolute head of the queue.       returnpostTimedEvent(event, INT64_MIN + 1);   }

TimedEventQueue::event_id TimedEventQueue::postTimedEvent(           constsp<Event> &event, int64_t realtime_us) {       Mutex::Autolock autoLock(mLock);         event->setEventID(mNextEventID++);         List<QueueItem>::iterator it = mQueue.begin();       while(it != mQueue.end() && realtime_us >= (*it).realtime_us) {           ++it;       }         QueueItem item;       item.event = event;       item.realtime_us = realtime_us;         if(it == mQueue.begin()) {           mQueueHeadChangedCondition.signal();       }         mQueue.insert(it, item);         mQueueNotEmptyCondition.signal();         returnevent->eventID();   }


 从代码可以看出,postEvent会调用postTimedEvent,而在postTimedEvent中第一步操作就是设置eventID,这也就保证了每个event都有唯一一个eventID标识

另外,会将事件通过mQueue.insert(it, item); 插入到TimedEventQueue->mQueue中,如下定义

struct QueueItem {       sp<Event> event;       int64_t realtime_us;   };

mQueue是一个QueueItem列表,存储了事件对象及要触生的时间

(2)每次mQueue都从列表中取出第一个事件进行处理,但有个问题,若此事件阻塞的时候,其他事件需要触发,怎么处理?

玄机同样在postTimedEvent中,在插入列表之前,会首先查找要插入到TimedEventQueue->mQueue 中的位置,按照触发时间排序

 

2.3 其他方法介绍

TimedEventQueue 中还有一些接口扩展使得其功能更加强大,主要有

event_id postEventToBack(const sp<Event> &event); 插入到事件列表末尾

TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(const sp<Event> &event, int64_t delay_us) ; 注册事件同时设置delay时间

bool TimedEventQueue::cancelEvent(event_id id);注销某个事件

void TimedEventQueue::cancelEvents(*);注销所有事件

原创粉丝点击