您的位置:首页 > 运维架构

安卓异步消息处理机制ALooper

2017-03-03 10:32 309 查看

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);
}

4000

  好了,前面介绍了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函数来完成注册了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ALooper loop post