ALooper簡(jiǎn)析

接著ALooper、AMessage入蛆、AHandler的簡(jiǎn)述,現(xiàn)在來(lái)分析下ALooper的聲明以及定義硕勿。

聲明

這篇重在細(xì)節(jié)哨毁,廢話不多說(shuō),各位看官搬好小板凳源武,且請(qǐng)看代碼(已加注釋)扼褪。

#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>

namespace android {
//前置聲明,在ALooper中只用到了這三個(gè)結(jié)構(gòu)體的指針或者引用粱栖。至于為什么要使用前置聲明话浇,我在C/C++專欄會(huì)寫
struct AHandler;
struct AMessage;
struct AReplyToken;

struct ALooper : public RefBase {
    typedef int32_t event_id;
    typedef int32_t handler_id;
    //構(gòu)造函數(shù)
    ALooper();

    // Takes effect in a subsequent call to start().
    //給這個(gè)Looper起名字
    void setName(const char *name);
    //將一個(gè)Handler注冊(cè)其中
    handler_id registerHandler(const sp<AHandler> &handler);
    //注銷對(duì)應(yīng)ID的AHandler
    void unregisterHandler(handler_id handlerID);
    //啟動(dòng)該Looper的獨(dú)立線程
    status_t start(
            bool runOnCallingThread = false,
            bool canCallJava = false,
            int32_t priority = PRIORITY_DEFAULT
            );
    //停止該Looper的獨(dú)立線程
    status_t stop();
    //獲得當(dāng)前的系統(tǒng)時(shí)間
    static int64_t GetNowUs();
    //獲取當(dāng)前Looper的名字
    const char *getName() const {
        return mName.c_str();
    }

protected:
    virtual ~ALooper();

private:
    friend struct AMessage;       // post()
    //包裝Message
    struct Event {
        int64_t mWhenUs;
        sp<AMessage> mMessage;
    };

    Mutex mLock;
    Condition mQueueChangedCondition;

    AString mName;

    List<Event> mEventQueue;
    //繼承于Thread
    struct LooperThread;
    sp<LooperThread> mThread;
    bool mRunningLocally;

    // use a separate lock for reply handling, as it is always on another thread
    // use a central lock, however, to avoid creating a mutex for each reply
    Mutex mRepliesLock;
    Condition mRepliesCondition;

    // START --- methods used only by AMessage

    // posts a message on this looper with the given timeout
    void post(const sp<AMessage> &msg, int64_t delayUs);

    // creates a reply token to be used with this looper
    sp<AReplyToken> createReplyToken();
    // waits for a response for the reply token.  If status is OK, the response
    // is stored into the supplied variable.  Otherwise, it is unchanged.
    status_t awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response);
    // posts a reply for a reply token.  If the reply could be successfully posted,
    // it returns OK. Otherwise, it returns an error value.
    status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg);

    // END --- methods used only by AMessage
    //如果有隊(duì)列中有消息,且未超過(guò)等待時(shí)間闹究,則將隊(duì)列頭的消息交付給對(duì)應(yīng)的handler處理
    bool loop();

    DISALLOW_EVIL_CONSTRUCTORS(ALooper);
};

下面幔崖,將就代碼中和結(jié)構(gòu)相關(guān)的部分簡(jiǎn)要分析一下。


struct Event {
    int64_t mWhenUs;
    sp<AMessage> mMessage;
    };

List<Event> mEventQueue;

可以看到渣淤,Event結(jié)構(gòu)體對(duì)Message以及該Message所對(duì)應(yīng)的入隊(duì)時(shí)間進(jìn)行的組裝赏寇。而后,又將這些進(jìn)入該Looper的消息進(jìn)行的一個(gè)排隊(duì)(用一個(gè)List包起來(lái))砂代。如果大家還記得之前《ALooper蹋订、AMessage、AHandler的簡(jiǎn)述》一篇的圖1的話刻伊,就能很好的理解露戒,為什么稱ALooper為消息隊(duì)列了。

定義


下面捶箱,將就代碼中和重要功能相關(guān)的部分簡(jiǎn)要分析一下智什。


1. void ALooper::post(const sp<AMessage> &msg, int64_t delayUs)

168    void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
169    Mutex::Autolock autoLock(mLock);
170
171    int64_t whenUs;
172    if (delayUs > 0) {
173        whenUs = GetNowUs() + delayUs;
174    } else {
175        whenUs = GetNowUs();
176    }
177
178    List<Event>::iterator it = mEventQueue.begin();
179    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
180        ++it;
181    }
182
183    Event event;
184    event.mWhenUs = whenUs;
185    event.mMessage = msg;
186
187    if (it == mEventQueue.begin()) {
188        mQueueChangedCondition.signal();
189    }
190
191    mEventQueue.insert(it, event);
192}

這個(gè)函數(shù)很簡(jiǎn)單。

  1. 根據(jù)設(shè)置的delayUS(延時(shí)交付時(shí)間)和NowUS(系統(tǒng)時(shí)間)來(lái)計(jì)算真正這條Message需要交付的時(shí)間丁屎。
  2. 之后再根據(jù)這個(gè)時(shí)間計(jì)算當(dāng)前Message在消息隊(duì)列中的位置荠锭。
  3. 如果該消息是隊(duì)列頭消息,就通知一下(等待中的線程中只有一個(gè)會(huì)被喚醒執(zhí)行)晨川。
  4. 如果不是就加入到隊(duì)列的相應(yīng)位置中败京。

2. status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg);

252    status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
253    Mutex::Autolock autoLock(mRepliesLock);
254    status_t err = replyToken->setReply(reply);
255    if (err == OK) {
256        mRepliesCondition.broadcast();
257    }
258    return err;
259}

先看看replyToken是個(gè)什么鬼(他住在AMessage的家里)

33    struct AReplyToken : public RefBase {
34    AReplyToken(const sp<ALooper> &looper)
35        : mLooper(looper),
36          mReplied(false) {
37    }

再看看setReply做了什么事情。

40   status_t AReplyToken::setReply(const sp<AMessage> &reply) {
41    if (mReplied) {
42        ALOGE("trying to post a duplicate reply");
43        return -EBUSY;
44    }
45    CHECK(mReply == NULL);
46    mReply = reply;
47    mReplied = true;
48    return OK;
49}

現(xiàn)在就很清楚了

  1. 創(chuàng)建一個(gè)和當(dāng)前Looper相關(guān)聯(lián)的AReplyToken 隐绵,并且初始化reply狀態(tài)為false缠局,就是還沒(méi)有回復(fù)。
  2. 之后將reply的消息和reply的狀態(tài)(變?yōu)閠rue妈拌,意為這個(gè)時(shí)候已經(jīng)回復(fù)了)給這個(gè)ReplyToken
  3. 如果成功返回OK拥坛,就廣播一下(等待的線程都會(huì)被喚醒)

3. status_t ALooper::start(bool runOnCallingThread, bool canCallJava, int32_t priority)

96    status_t ALooper::start(
97        bool runOnCallingThread, bool canCallJava, int32_t priority) {
98    if (runOnCallingThread) {
99        {
100            Mutex::Autolock autoLock(mLock);
101
102            if (mThread != NULL || mRunningLocally) {
103                return INVALID_OPERATION;
104            }
105
106            mRunningLocally = true;
107        }
108
109        do {
110        } while (loop());
111
112        return OK;
113    }
114
115    Mutex::Autolock autoLock(mLock);
116
117    if (mThread != NULL || mRunningLocally) {
118        return INVALID_OPERATION;
119    }
120
121    mThread = new LooperThread(this, canCallJava);
122
123    status_t err = mThread->run(
124            mName.empty() ? "ALooper" : mName.c_str(), priority);
125    if (err != OK) {
126        mThread.clear();
127    }
128
129    return err;
130}

首先,我們可以知道這是該Looper線程啟動(dòng)的函數(shù). 當(dāng)設(shè)置了runOnCallingThread為true的時(shí)候, 對(duì)應(yīng)邏輯里面有一個(gè)重要的代碼段:

    do {
    } while (loop());

那這個(gè)loop() 是干嘛用的? 我們來(lái)看一看.

194    bool ALooper::loop() {
195    Event event;
196
197    {
198        Mutex::Autolock autoLock(mLock);
199        if (mThread == NULL && !mRunningLocally) {
200            return false;
201        }//如果沒(méi)有分到線程且沒(méi)有在本地運(yùn)行,返回錯(cuò)誤
202        if (mEventQueue.empty()) {
203            mQueueChangedCondition.wait(mLock);
204            return true;
205        }
206        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
207        int64_t nowUs = GetNowUs();
208
209        if (whenUs > nowUs) {
210            int64_t delayUs = whenUs - nowUs;
211            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
212
213            return true;
214        }
215
216        event = *mEventQueue.begin();
217        mEventQueue.erase(mEventQueue.begin());
218    }
219
220    event.mMessage->deliver();
221
222    // NOTE: It's important to note that at this point our "ALooper" object
223    // may no longer exist (its final reference may have gone away while
224    // delivering the message). We have made sure, however, that loop()
225    // won't be called again.
226
227    return true;
228}

重要的有以下幾點(diǎn)

    if (mEventQueue.empty()) {
        mQueueChangedCondition.wait(mLock);
        return true;
     }

不知道大家是否還記得,之前講異步消息機(jī)制簡(jiǎn)述那一篇中, 有提到過(guò)說(shuō), ALooper會(huì)檢查消息隊(duì)列中消息是否為空, 如果為空, 則等待事件, 降低CPU資源的消耗. 那么這段代碼就是上面所說(shuō)的具體實(shí)現(xiàn)了.

之后, 會(huì)判斷whenUS和nowUS, 根據(jù)這兩個(gè)值的比較來(lái)判斷是否還需要等待一段時(shí)間處理Event.

緊接著, 用上面定義了的Event event, 拿到消息隊(duì)列的列頭, 并清除原隊(duì)列的列頭.

最后, 交付這個(gè)消息. (這個(gè)交付的實(shí)現(xiàn), 會(huì)在AMessage中講解)

好回到start()代碼的接下來(lái)的流程中. 接下來(lái)就很簡(jiǎn)單了:

  1. 創(chuàng)建一個(gè)LooperThread的實(shí)例;
  2. 把這個(gè)實(shí)例線程跑起來(lái).

接下來(lái),講兩個(gè)和AMessage::postAndAwaitResponse方法緊密相連的兩個(gè)方法. 一個(gè)是ALooper::createReplyToken(), 而另外一個(gè)是ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response)

首先, 來(lái)了解下

4. ALooper::createReplyToken()

這個(gè)玩意兒的代碼如下:

230    // to be called by AMessage::postAndAwaitResponse only
231    sp<AReplyToken> ALooper::createReplyToken() {
232    return new AReplyToken(this);
233    }

沒(méi)錯(cuò), 沒(méi)干什么事情, 只是用當(dāng)前的Looper, 實(shí)例化了一個(gè)AReplyToken(在第2點(diǎn)鐘有提到這個(gè)東東)并返回.

接著,來(lái)看這個(gè)

5. ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response)

它長(zhǎng)這個(gè)樣子:

236    status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
237    // return status in case we want to handle an interrupted wait
238    Mutex::Autolock autoLock(mRepliesLock);
239    CHECK(replyToken != NULL);
240    while (!replyToken->retrieveReply(response)) {
241        {
242            Mutex::Autolock autoLock(mLock);
243            if (mThread == NULL) {
244                return -ENOENT;
245            }
246        }
247        mRepliesCondition.wait(mRepliesLock);
248    }
249    return OK;
250}

我們遇到了陌生的retrieveReply, 他長(zhǎng)這個(gè)樣子:

49    // if reply is not set, returns false; otherwise, it retrieves the reply and returns true
50    bool retrieveReply(sp<AMessage> *reply) {
51        if (mReplied) {
52            *reply = mReply;
53            mReply.clear();
54        }
55        return mReplied;
56    }

原生注釋已經(jīng)說(shuō)得很清楚了. 如果replay沒(méi)有設(shè)置,就返回false; 否則, 就獲得這個(gè)replay的Message并返回true.

回到awaitResponse中. 當(dāng)retrieveReply返回false, 也就是說(shuō)沒(méi)有拿到需要replay的Message的時(shí)候, 等待著, 一直等待到拿到這個(gè)消息為止(各位看官, 這里就是為了實(shí)現(xiàn)postAndAwaitResponse的同步的目的了哦)


總結(jié)

這篇講述了一個(gè)叫ALooper的履帶。它將AMessage根據(jù)交付時(shí)間(whenUs)進(jìn)行排隊(duì)(List<mEvent>)。然后簡(jiǎn)單介紹了它是怎么將消息(AMessage交付的(有同步的, 也有異步的))猜惋。關(guān)于交付這一塊是不完整的丸氛,大部分的邏輯在AMessage中,今天先介紹到這里著摔,有空再寫最復(fù)雜的AMessage和相對(duì)簡(jiǎn)單的AHandler缓窜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市梨撞,隨后出現(xiàn)的幾起案子雹洗,更是在濱河造成了極大的恐慌,老刑警劉巖卧波,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件时肿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡港粱,警方通過(guò)查閱死者的電腦和手機(jī)螃成,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)查坪,“玉大人寸宏,你說(shuō)我怎么就攤上這事〕ナ铮” “怎么了氮凝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)望忆。 經(jīng)常有香客問(wèn)我罩阵,道長(zhǎng),這世上最難降的妖魔是什么启摄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任稿壁,我火速辦了婚禮,結(jié)果婚禮上歉备,老公的妹妹穿的比我還像新娘傅是。我一直安慰自己,他們只是感情好蕾羊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布喧笔。 她就那樣靜靜地躺著,像睡著了一般龟再。 火紅的嫁衣襯著肌膚如雪溃斋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天吸申,我揣著相機(jī)與錄音,去河邊找鬼。 笑死截碴,一個(gè)胖子當(dāng)著我的面吹牛梳侨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播日丹,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼走哺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哲虾?” 一聲冷哼從身側(cè)響起丙躏,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎束凑,沒(méi)想到半個(gè)月后晒旅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汪诉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年废恋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扒寄。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鱼鼓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出该编,到底是詐尸還是另有隱情迄本,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布课竣,位于F島的核電站嘉赎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏稠氮。R本人自食惡果不足惜曹阔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隔披。 院中可真熱鬧赃份,春花似錦、人聲如沸奢米。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鬓长。三九已至谒拴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涉波,已是汗流浹背英上。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工炭序, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苍日。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓惭聂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親相恃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辜纲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容