Android異步消息框架

原文鏈接:http://blog.sina.com.cn/s/blog_645b74b90101cx69.html


自從rtsp從stagefright播放器移植到NuPlayer之後,你會發現相關的類中存在許多類似下面的代碼:

================================11111111111111==========================
NuPlayerDriver::NuPlayerDriver()
     :mLooper(new ALooper) {
   mLooper->setName("NuPlayerDriver Looper");
   mLooper->start(
          false,
           true, 
          PRIORITY_AUDIO);
    mPlayer = newNuPlayer;
   mLooper->registerHandler(mPlayer);
}
====================222222222222222=============================
    sp msg = newAMessage(kWhatPerformSeek, mReflector->id());
   msg->setInt32("generation",++mSeekGeneration);
   msg->setInt64("timeUs", seekTimeUs);
   msg->post(200000ll);
=====================333333333333333============================
    voidNuPlayer::RTSPSource::onMessageReceived(const sp&msg) {
    }
=================================================================
這就是谷歌在Androidnative層實現的一個異步消息機制,在這個機制中幾乎不存在同步鎖,所有的處理都是異步的,將變量封裝到一個消息AMessage結構體中,然後放到隊列中去,後臺專門有一個線程會從這個隊列中取出消息然後執行,執行函數就是onMessageReceived,這個函數中會有很多分支,用於處理不同的消息;在很多類中都會有各種消息post出來,而後臺的異步消息處理線程又是怎麼知道發送給哪個類的onMessageReceived函數處理呢,要搞懂這個問題,就需要把谷歌實現的這個異步消息處理框架搞明白,下面就來分析這個框架。

一:消息
異步消息處理框架,消息是載體,所以我們先了解消息這個類
AMessage類
struct AMessage : public RefBase {
   構造函數,包括兩個參數,第一個參數指明這是個什麼消息,用於在onMessageReceived處理分支中進行匹配,第二個參數target用於後臺線程在處理這個消息的時候知道發給哪個類處理,後面看構造這個消息的時候是這兩個參數是怎麼傳進來的
   AMessage(uint32_t what = 0, ALooper::handler_id target =0);
    void setWhat(uint32_twhat);
    uint32_t what()const;
    voidsetTarget(ALooper::handler_id target);
    ALooper::handler_idtarget() const;

    void clear();
   這個消息類中定義了一堆set和find方法,用於在在傳遞消息過程中攜帶各種信息
   void setObject(const char *name, const sp&obj);
   void setBuffer(const char *name, const sp&buffer);
   void setMessage(const char *name, const sp&obj);
   bool findBuffer(const char *name, sp *buffer) const;
   bool findMessage(const char *name, sp *obj) const;
    void post(int64_tdelayUs = 0);
protected:
    virtual ~AMessage(); 析構函數
private:
   uint32_t mWhat;
   ALooper::handler_id mTarget;
   兩個重要的私有成員變量
};
正如開頭部分看的那樣,構造一個消息的過程如下:
void NuPlayer::start() {
    (newAMessage(kWhatStart, id()))->post();
}
void AMessage::post(int64_t delayUs) {
   gLooperRoster.postMessage(this, delayUs);
}
post之後發生的事情由gLooperRoster負責,我們暫且不管。


二:框架搭建
現在來看處理異步消息框架的搭建,這裏在copy一段搭建框架的幾行代碼
  1    sp《ALooper》mNetLooper;
  2     mNetLooper(newALooper),
  3    mNetLooper->setName("rtspnet");
  4    mNetLooper->start(false,
  5     (1 ? mNetLooper :looper())->registerHandler(mRTPConn);

ALooper類
struct ALooper : public RefBase {
   typedef int32_t event_id;
   typedef int32_t handler_id;
   定義的兩個整形變量
    ALooper();
    // Takes effect in asubsequent call to start().
    void setName(const char*name);
    handler_idregisterHandler(const sp &handler);
    voidunregisterHandler(handler_id handlerID);
    status_t start(
           boolrunOnCallingThread = false,
           boolcanCallJava = false,
           int32_tpriority = PRIORITY_DEFAULT
           );
private:
   friend struct ALooperRoster;這個類比較重要,後面消息post的流程和其相關
   事件的結構體封裝
    struct Event {
       int64_t mWhenUs;
       sp mMessage;
    };
   非常重要,後臺存放事件的鏈表
    List《Event》mEventQueue;
   struct LooperThread;
    spmThread;
   後臺處理線程
    void post(const sp&msg, int64_t delayUs);
    bool loop();
};

搭建框架1
ALooper::ALooper()
    : mRunningLocally(false){
}
搭建框架2
void ALooper::setName(const char *name) {
    mName = name; //AString mName;
}
搭建框架3
status_t ALooper::start(
       bool runOnCallingThread, bool canCallJava,int32_t priority) {
   開啓真正的後臺處理線程
   mThread = new LooperThread(this, canCallJava);
   status_t err = mThread->run(
          mName.empty() ? "ALooper" : mName.c_str(),priority);給這個線程設置一個名字
    return err;
} run之後就會執行其線程函數threadLooper(),後面有分析。

搭建框架4-1
ALooper::handler_id ALooper::registerHandler(constsp括號AHandler括號 &handler) {
   return gLooperRoster.registerHandler(this,handler);
}
從這個函數的參數來看,能作爲handler進行註冊的類都必須是繼承自AHandler這個類,註冊的過程也是交給
gLooperRoster處理。

AHandler類分析----消息處理類的父類
因爲後面要用到AHandler裏面的一個非常重要的函數id(),所以我們先分析一下AHandler這個公共的父類:
struct AMessage;
struct AHandler : public RefBase {
    AHandler()
       : mID(0) {  mID的初始值爲0
    }
   ALooper::handler_id id() const {
      return mID; id()這個函數用於返回內部變量mID的值,其初始值爲0,但是會通過setID函數設置
    }
    sp looper();
protected:
    virtual voidonMessageReceived(const sp &msg) = 0;
private:
    friend structALooperRoster;
   ALooper::handler_id mID;
   下面這個函數正式在其友元類ALooperRoster的registerHandler中調用的
   void setID(ALooper::handler_id id) {
       mID = id;
   }
};

ALooperRoster類分析
post消息和註冊handler都要用到gLooperRoster這麼一個對象,那下面我就要分析對應的ALooperRoster類
struct ALooperRoster {
    ALooperRoster();
   ALooper::handler_id registerHandler(
          const sp括號ALooper括號 looper, constsp括號AHandler括號 &handler);
    voidunregisterHandler(ALooper::handler_id handlerID);
   status_t postMessage(const sp &msg,int64_t delayUs = 0);
    voiddeliverMessage(const sp &msg);
    status_tpostAndAwaitResponse(
           const sp&msg, sp *response);
    void postReply(uint32_treplyID, const sp &reply);
    spfindLooper(ALooper::handler_id handlerID);
private:
    struct HandlerInfo{
       wp mLooper;
       wp mHandler;
    };
    Mutex mLock;
    KeyedVectormHandlers;
    ALooper::handler_idmNextHandlerID;
    uint32_tmNextReplyID;
    ConditionmRepliesCondition;
    KeyedVector> mReplies;
    status_tpostMessage_l(const sp &msg, int64_tdelayUs);
};
然後來看一些關鍵函數的實現
搭建框架4-2
ALooper::handler_id ALooperRoster::registerHandler(
       const sp looper, const sp&handler) {
    Mutex::AutolockautoLock(mLock);
   還記得上面我們構造一個AMessage的時候有一個參數就是調用id()這個函數返回的一個ALooper::handler_id這個無符號整形變量,id()是AHandler這個類的一個函數,上面已經有分析,其返回的是各個Handler的mID的值,那麼這個值是在哪裏設置的呢?請繼續往下看。
    if (handler->id() != 0) {
       CHECK(!"A handler must only be registeredonce.");
       return INVALID_OPERATION;
    }
    HandlerInfo info;
    info.mLooper =looper;
    info.mHandler =handler;
   ALooper::handler_id handlerID =mNextHandlerID++;
   mHandlers.add(handlerID, info);
   handler->setID(handlerID);
   這段代碼非常重要,針對每個handler調用registerHandler的時候都會設置一個獨一無二的handler_id,最後發送消息進行處理的時候就是通過這個獨一無二的handler_id這個變量找到處理handler類的。
    return handlerID;
   在這個函數中針對這個looper和handler會構造一個HandlerInfo結構體,然後放到pair容器中。這裏說一下,一個looper可以有多個handler,但是一一個handler只能跟一個looper。
}
***********************************************************************
好了框架搭建好了,下面就是消息的傳遞處理過程了,這個留着下回更新。
明天又是週一,早睡早起,好好工作,好好研究數據結構。
===========================================================================
數據結構看到非常經典的幾章,本想在博客上好好整理一下學習成果,但是鑑於每一篇博客都不能有始無終,所以還是先把這篇博客補充完整吧
*************************************************************************

四:消息處理
第一部分,我們看到post之後調用了ALooperRoster的postMessage函數
status_tALooperRoster::postMessage(
       constsp<AMessage> &msg,int64_t delayUs) {
   Mutex::Autolock autoLock(mLock);
   return postMessage_l(msg, delayUs);
}
status_tALooperRoster::postMessage_l(
       constsp<AMessage> &msg,int64_t delayUs) {
   msg->target() 返回的是id,在上面registerHandler過程中,每個id和其對應的info組成一個pair放到了mHandlers這個容器中,現在通過這個id在找回這個pair
   ssize_t index = mHandlers.indexOfKey(msg->target()); 
    if(index < 0) {
       ALOGW("failed to postmessage. Target handler not registered.");
       return -ENOENT;
   }
   找到這個pair的位置index,然後取出對應的HandlerInfo,有了HandlerInfo,也就能知道對應的ALooper了
   const HandlerInfo &info =mHandlers.valueAt(index);
   sp<ALooper> looper =info.mLooper.promote();
   looper->post(msg, delayUs);
   return OK;
}
void ALooper::post(constsp<AMessage> &msg,int64_t delayUs) {
   Mutex::Autolock autoLock(mLock);
   int64_t whenUs;
   if (delayUs > 0) {
       whenUs =GetNowUs() + delayUs;
   } else {
       whenUs =GetNowUs();
   }
  List<Event>::iterator it =mEventQueue.begin();
   while (it != mEventQueue.end()&& (*it).mWhenUs <=whenUs) {
      ++it;
   }
  上面這一段代碼,主要是遍歷消息隊列中的消息的時間,然後和我們的消息做對比,最終目的就是所有的消息必須按照時間先後順序放在隊列中等待執行。
   構造一個Event消息,將我們的msg設置進去
   Event event;
   event.mWhenUs = whenUs;
   event.mMessage = msg;
   將我們的消息放入到消息隊列中,等待執行
   mEventQueue.insert(it, event);
}

五:後臺線程執行消息
還記得我們在ALooper的start函數中啓動了一個後臺線程嗎,下面我們來分析它
status_t ALooper::start(
       bool runOnCallingThread, bool canCallJava,int32_t priority) {
    ........
    mThread = newLooperThread(this, canCallJava);
   status_t err = mThread->run(
          mName.empty() ? "ALooper" : mName.c_str(),priority);
    if (err != OK) {
       mThread.clear();
    }
    return err;
}
LooperThread線程啓動後,就會循環執行線程函數threadLoop,我們看其實現:
   virtual bool threadLoop() {
       returnmLooper->loop();
   }
調用start時傳進來的ALooper的looper方法,
    bool ALooper::loop(){
    Event event;
    {
       Mutex::Autolock autoLock(mLock);
       ........
      取出隊頭消息,取出後然後把它從隊列中刪掉,然後調用gLooperRoster的deliverMessage進行發送出去執行
       event = *mEventQueue.begin();
       mEventQueue.erase(mEventQueue.begin());
    }
   gLooperRoster.deliverMessage(event.mMessage);
   這個函數如果返回true,則線程會不斷執行又會進入到這個函數中來,如果返回false,線程就會停止了
    return true;
}
void ALooperRoster::deliverMessage(constsp<AMessage> &msg){
   sp<AHandler> handler;
    {
       Mutex::Autolock autoLock(mLock);
      通過這個消息的id找到保存在mHandlers中的HandlerInfo,然後從HandlerInfo中取出對應的handler,然後調用這個handler這個的onMessageReceived方法進行處理消息。
      ssize_t index =mHandlers.indexOfKey(msg->target());
       const HandlerInfo&info = mHandlers.valueAt(index);
       handler = info.mHandler.promote();
    }
   handler->onMessageReceived(msg);
}
如在NuPlayer.cpp中
void NuPlayer::onMessageReceived(constsp<AMessage> &msg){
    switch(msg->what()) {
       case kWhatSetDataSource:
       case kWhatVideoNotify:
       case kWhatAudioNotify:
       case kWhatRendererNotify:
       case kWhatMoreDataQueued:
       case kWhatReset:
       case kWhatSeek:
       case kWhatPause:
       case kWhatResume:
       default:
    }
}
各個case中的條件就對應msg中的what了,根據不同的what,走不同的case處理。

至此,整個消息的處理過程完畢,總結如下:
1:首先需要構造一個AMessage,必須攜帶兩個參數:what(什麼消息)和id(誰處理);
2:ALooper中有一個後臺線程,LooperThread,該線程維護着一個消息隊列List<Event>mEventQueue,線程函數不斷從這個隊列中取出消息執行;
3:消息的發送和取出都是調用輔助類ALooperRoster,這個類設置爲各個類的友元類,可以方位ALooper,AMessage,AHandler等的受保護的函數。

發佈了30 篇原創文章 · 獲贊 2 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章