安卓异步消息处理机制ALooper

1.下面贴出安卓N版本ALooper.h的原文

#ifndef A_LOOPER_H_

#define A_LOOPER_H_

#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 {

struct AHandler;
struct AMessage;
struct AReplyToken;

struct ALooper : public RefBase {
    typedef int32_t event_id;
    typedef int32_t handler_id;

    ALooper();

    // Takes effect in a subsequent call to start().
    void setName(const char *name);

    handler_id registerHandler(const sp<AHandler> &handler);
    void unregisterHandler(handler_id handlerID);

    status_t start(
            bool runOnCallingThread = false,
            bool canCallJava = false,
            int32_t priority = PRIORITY_DEFAULT
            );

    status_t stop();

    static int64_t GetNowUs();

    const char *getName() const {
        return mName.c_str();
    }

protected:
    virtual ~ALooper();

private:
    friend struct AMessage;       // post()

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

    Mutex mLock;
    Condition mQueueChangedCondition;

    AString mName;

    List<Event> mEventQueue;

    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

    bool loop();

    DISALLOW_EVIL_CONSTRUCTORS(ALooper);
};

} // namespace android

#endif  // A_LOOPER_H_

1.1条件编译

#ifndef A_LOOPER_H_

#define A_LOOPER_H_

...
...
...
#endif  // A_LOOPER_H_

  条件编译宏,防止重复编译。

1.2包含的头文件

#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>

1.3名字空间

namespace android {

...
...
...

}

2.重要结构体struct Event

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

    List<Event> mEventQueue;

  结构体struct Event有两个域,一个域是一个64bits的整数,是消息发送过来时给消息打的时间戳。

  打的时间戳计算方法为:mWhenU=GetNowUs() + delayUs (delayUs > 0),即当用户设置的延时delayUs大于0的时候,给消息打的时间戳为系统时间(GetNowUs())加上用户设置的延时(delayUs)。

  如果用户设置的延时delayUs小于等于0的时候,则直接采用系统时间(GetNowUs())给该消息打时间戳:whenUs = GetNowUs()

  List< Event > mEventQueue,将所有发送来的消息打上时间戳后封装成事件都放到mEventQueue链表里,注意事件在链表里存放的顺序是按打的时间戳递增顺序来存放的。由于会频繁的向事件链表里插入事件,采用链式存储的表List是最合适的,因为插入和删除动作能在O(1)事件内完成。

3.ALooper::post函数的实现

  下面贴出安卓N版本ALooper::post函数的实现

void ALooper::post(const sp<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 event;
    event.mWhenUs = whenUs;
    event.mMessage = msg;

    if (it == mEventQueue.begin()) {
        mQueueChangedCondition.signal();
    }

    mEventQueue.insert(it, event);
}

  好了,前面介绍了ALooper的消息存储机制,现在来看下ALopper是如何设计外部接口来向事件链表里添加消息对应的事件的吧。

3.1给该消息计算时间戳

    int64_t whenUs;
    if (delayUs > 0) {
        whenUs = GetNowUs() + delayUs;
    } else {
        whenUs = GetNowUs();
    }

  这个在前面已经讲到了,即根据delayUs的值来选择不同的计算时间戳的方法。

3.2计算该事件该插入的位置

    List<Event>::iterator it = mEventQueue.begin();
    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
        ++it;
    }

  这个很显然咯,就是从链表第一个位置,不停地迭代,直到找到第一个时间戳大于该事件的时间戳的位置it.然后将该事件插入到it的前面。这个很简单咯,但是真的很基础,数据结构链表的知识,就这样简单的迭代计算,大量地在安卓系统源码里用到。

3.3封装成事件

    Event event;
    event.mWhenUs = whenUs;
    event.mMessage = msg;

  就是分别用计算得到的时间戳,和消息的引用来填充Event结构体的两个域。来完成事件的封装。

3.4事件添加到链表里

mEventQueue.insert(it, event);

  就是将该事件插入到it前面。it是之前计算得到的位置。

4.ALooper::loop函数的实现

  下面贴出安卓N版本ALooper::loop函数实现源码:

bool ALooper::loop() {
    Event event;

    {
        Mutex::Autolock autoLock(mLock);
        if (mThread == NULL && !mRunningLocally) {
            return false;
        }
        if (mEventQueue.empty()) {
            mQueueChangedCondition.wait(mLock);
            return true;
        }
        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
        int64_t nowUs = GetNowUs();

        if (whenUs > nowUs) {
            int64_t delayUs = whenUs - nowUs;
            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);

            return true;
        }

        event = *mEventQueue.begin();
        mEventQueue.erase(mEventQueue.begin());
    }

    event.mMessage->deliver();

    // NOTE: It's important to note that at this point our "ALooper" object
    // may no longer exist (its final reference may have gone away while
    // delivering the message). We have made sure, however, that loop()
    // won't be called again.

    return true;
}

  这个loop函数将被指定成函数入口,通过安卓系统实现的线程机制该入口函数将会不断地被调用。通俗的来说就是这个loop函数会不断低被执行,可以这样理解do { loop();} while(1);,但是真实的实现机制是比较复杂滴,后续的文章有机会的话会像大家介绍。
  好了,这样就是建立了一个循环,在该循环里不断地将链表里第一个事件,也就是时间戳最小的事件,取出该事件,然后将该事件的消息派发出去。这样的设计很自然,因为时间戳越小说明该消息到达得越早,显然需要被优先处理。
 

4.1循环派发消息

        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
        int64_t nowUs = GetNowUs();

        if (whenUs > nowUs) {
            int64_t delayUs = whenUs - nowUs;
            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);

            return true;
        }

        event = *mEventQueue.begin();
        mEventQueue.erase(mEventQueue.begin());

  首先得到链表第一个事件的时间戳,然后得到当前系统的时间戳,如果事件的事件戳小于或者等于系统时间,说明该事件需要被派发出去不需要在链表里继续等待了。则跳过if (whenUs > nowUs)分支。
  
  取出链表第一个事件,然后调用该事件消息的deliver()函数将该消息派发出去: 

   event.mMessage->deliver();

5.ALooper::registerHandler

  下面贴出安卓N版本ALooper::registerHandler函数实现的源码:

ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
    return gLooperRoster.registerHandler(this, handler);
}

  ALooper::registerHandler函数是通过调用全局对象gLooperRoster的registerHandler函数来实现的。当调用全局对象gLooperRoster的registerHandler函数,是传递的参数是,自身即消息链表的维护者(ALooper)引用和消息处理者(AHandler)。这样就将ALooper和AHandler绑定成一对儿来通过全局对象gLooperRoster的registerHandler函数来完成注册了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值