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函数来完成注册了。