AHandler AMessage ALooper消息机制
最近学习了一下Android Media相关的代码,因此整理了一下Media通过AHandler AMessage ALooper来实现消息发送的消息机制。
AHandler是Android native层实现的一个异步消息机制,在这个机制中所有的处理都是异步的,将变量封装到一个消息AMessage结构体中,然后放到队列中去,后台专门有一个线程ALooper会从这个队列中取出消息然后分发执行,AHandler中的纯虚函数onMessageReceived,我们定义的子类就是通过实现这个方法去执行消息处理。
因为一个Handler并不是给一个Message专用的,可能会处理很多种的消息,那么为了区分消息,便加入一个what值来告诉Handler现在的消息是哪种,一般Handler会使用一个switch来进行处理会根据what值来判断需要进行什么操作。
1.涉及的主要类
主要的涉及的有以下三个类
- AMessage
消息类,用于构造消息,通过post方法投递出去给ALooper - ALooper
轮询类,用于保存消息队列,然后将消息发送到对应的AHandler - AHandler
处理类,用于实际消息的处理
ALooper和AHandler相对都是比较简单的,剩下一个AMessage反倒是这个消息队列中比较复杂的一个组成部分了,AMessage不仅仅是传递消息这么简单,它可以附带上很多的键值对在身上,也就是说这个Message在传递的时候除了最重要的what值之外还能带上一些其他的值,而每个值都有一个对应的key,这样接收方在处理消息时可以根据这个key值拿出在Message中存储的value,这样一次可以传递的信息就大大增加了。
2.消息类型
发送的消息按照是否需要答复,分为两种
- 普通消息
- 需答复消息
3.模型简介
当我们想要我们的程序执行一些事情的时候要么同步,要么异步,而消息机制便是一种异步的方式,首先创建一个消息队列,这个消息队列在一个单独的线程中轮询,一旦有人发送消息给它,它就会将消息取出执行,执行后又回到等待状态直到有新的消息到来
普通消息被Post到消息队列,Looper不断的从队列中取出消息并分发给相应的Handler处理。如果是需答复消息的话使用PostAndAwaitResponse,Handler处理完以后通过postReply将消息返回给发送方,发送方通过新建的responsemsg接收。
4.代码解析
4.1初始化
1.注册Handler
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}
因此实际是ALooperRoster的registerHandler,生成一个handler ID并保存起来
ALooper::handler_id ALooperRoster::registerHandler(
const sp<ALooper> &looper, const sp<AHandler> &handler) {
Mutex::Autolock autoLock(mLock);
if (handler->id() != 0) {
CHECK(!"A handler must only be registered once."); //一个handler在同一个looper中只能注册一次
return INVALID_OPERATION;
}
HandlerInfo info; //HandlerInfo 结构体,保存looper和handler,将其绑定在一起
info.mLooper = looper;
info.mHandler = handler;
ALooper::handler_id handlerID = mNextHandlerID++; //生成handler ID
mHandlers.add(handlerID, info); //mHandlers为ALooperRoster的私有,保存handler列表
handler->setID(handlerID, looper);
return handlerID;
}
2.Looper循环
status_t ALooper::start(
bool runOnCallingThread, bool canCallJava, int32_t priority) {
// ...
Mutex::Autolock autoLock(mLock);
if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
}
mThread = new LooperThread(this, canCallJava);
status_t err = mThread->run(
mName.empty() ? "ALooper" : mName.c_str(), priority);
if (err != OK) {
mThread.clear();
}
return err;
}
4.2消息传递
ALooper和AHandler相对代码量较少,相对Amessage是三个中较为复杂的模块,Amessage不仅传递了最重要的what,而且还带上了其他的key-value值,这样在接受消息时,可以根据key拿到Message的Value。
Amessage有个内部类实现了key-Value的定义:
struct Item {
union {
int32_t int32Value;
int64_t int64Value;
size_t sizeValue;
float floatValue;
double doubleValue;
void *ptrValue;
RefBase *refValue;
AString *stringValue;
Rect rectValue;
} u;
const char *mName;
size_t mNameLength;
Type mType;
void setName(const char *name, size_t len);
};
使用联合体这段空间来存储value,使用mName来作为key。mNameLength来存储key值得长度,mType是一个枚举值,保存了所存数据类型,定义如下:
enum Type {
kTypeInt32,
kTypeInt64,
kTypeSize,
kTypeFloat,
kTypeDouble,
kTypePointer,
kTypeString,
kTypeObject,
kTypeMessage,
kTypeRect,
kTypeBuffer,
};
Amessage的构造方式有两种,一般使用的是第二种,传入消息的what值和handler,随后调用setTarget通过Handler赋值。
AMessage::AMessage(void)
AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
梳理一下Amessage的消息流程:
- 实现一个AHandler的子类,主要是onMessageReceived方法
- 创建一个ALooper对象
- 创建消息AMessage进行投递Post
- onMessageReceived方法得到调用,处理实际的事务
(-----需应答消息-----) - 处理完消息后发送答复postReply
- 发送端接收答复消息response
分析的重点是AMessage的post方法,通过调用了ALooper的post方法。之后Looper函数会把消息取出来调用Message的deliver方法。
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(); //如果之前消息队列为空,没有event,loop是处于阻塞状态的,此处当有消息进入时,先通知looperthread启动
}
mEventQueue.insert(it, event); //进入消息队列
}
looper循环进行分发
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(); //根据每个消息携带的handler做转发处理
// 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;
}
void AMessage::deliver() {
sp<AHandler> handler = mHandler.promote();
if (handler == NULL) {
ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
return;
}
handler->deliverMessage(this);
}
转发到相应的Handler,Handler实例必须覆盖onMessageReceived方法,在这里做对应的处理,一般采用switch语句进行不同的处理
void AHandler::deliverMessage(const sp<AMessage> &msg) {
onMessageReceived(msg);
mMessageCounter++;
if (mVerboseStats) {
uint32_t what = msg->what();
ssize_t idx = mMessages.indexOfKey(what);
if (idx < 0) {
mMessages.add(what, 1);
} else {
mMessages.editValueAt(idx)++;
}
}
}
目前为止,普通消息的发送流程已经结束了。已经能够正常进入Handler消息处理的流程。
接下来看下需要答复的消息是如何发送的:
//发送/接收答复
sp<AMessage> msg = new AMessage(kWhatSaySomething, mHandler); //发送的消息
sp<AMessage> responsemsg; //接收存储答复消息
msg->postAndAwaitResponse(responsemsg);
//处理/答复
AHandlerReflector:: onMessageReceived //消息的具体的处理实现
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
sp<AMessage> response = new AMessage;//创建答复消息
response->postReply(replyID);//发送答复消息
其post过程和普通消息的一致,先发送到looper,然后loop分发给对应的handler处理。
不同的是得先调用createReplyToken创建了一个token,然后再通过mRepliesCondition.wait(mRepliesLock)阻塞等待。
status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
sp<ALooper> looper = mLooper.promote();
if (looper == NULL) {
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
return -ENOENT;
}
sp<AReplyToken> token = looper->createReplyToken(); //创建Token标识,此token用来唯一的标识这条信息
if (token == NULL) {
ALOGE("failed to create reply token");
return -ENOMEM;
}
setObject("replyID", token); //将此token添加到该Message的replyID字段
looper->post(this, 0 /* delayUs */); //发给looper
return looper->awaitResponse(token, response); //在此处就阻塞住,不返回了
}
等收到mRepliesCondition.broadcast()以后,会执行retrieveReply,它的作用是将返回的reply拷贝到response
status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
// return status in case we want to handle an interrupted wait
Mutex::Autolock autoLock(mRepliesLock);
CHECK(replyToken != NULL);
while (!replyToken->retrieveReply(response)) { //第一次会进入这个循环
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL) {
return -ENOENT;
}
}
mRepliesCondition.wait(mRepliesLock); //等待直到收到答复广播
}
return OK;
}
// if reply is not set, returns false; otherwise, it retrieves the reply and returns true
bool retrieveReply(sp<AMessage> *reply) {
if (mReplied) {
*reply = mReply;
mReply.clear();
}
return mReplied;
}
所以上面的程序就在等待mRepliesCondition.broadcast()的发出,消息的处理,也和普通消息一致,在相应的handler实例的onMessageReceived中得到处理,处理完以后创建Amessage答复消息
status_t AMessage::postReply(const sp<AReplyToken> &replyToken) {
if (replyToken == NULL) {
ALOGW("failed to post reply to a NULL token");
return -ENOENT;
}
sp<ALooper> looper = replyToken->getLooper();//根据replyToken获取消息所在的looper
if (looper == NULL) {
ALOGW("failed to post reply as target looper is gone.");
return -ENOENT;
}
return looper->postReply(replyToken, this); //转到looper
}
在looper这里首先将reply消息拷贝的位于AReplyToken的一个缓冲变量mReply,然后发出回复成果广播
status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
Mutex::Autolock autoLock(mRepliesLock);
status_t err = replyToken->setReply(reply); //将回复消息保存
if (err == OK) {
mRepliesCondition.broadcast(); //发出已回复广播
}
return err;
}
保存回复信息到缓存变量mReply
status_t AReplyToken::setReply(const sp<AMessage> &reply) {
if (mReplied) {
ALOGE("trying to post a duplicate reply");
return -EBUSY;
}
CHECK(mReply == NULL);
mReply = reply; //保存回复信息
mReplied = true;
return OK;
}