核心三元素
- AHandler
- ALooper
- AMessage
类图
三者如何建立联系?(以 MediaCodec 初始化为例)
Handler 和 Looper 绑定
我们目前所知的是 AHandler 是处理消息,AMessage 代表消息本身,而 ALooper 负责管理消息队
列,接下来介绍三者之间是如何建立联系,让消息能被对应的的 handler 接收到并处理。
从 java 层开始,用户使用的 MediaCodec 的时候,会使用 MediaCodec.createByCodecName 来创建
实例,这个函数在 JNI 中的代码有
mLooper = new ALooper;
mLooper->setName("MediaCodec_looper");//创建以 MediaCodec_looper 为名的
ALooper 实例
mLooper->start(
false, // runOnCallingThread
true, // canCallJava |
ANDROID_PRIORITY_VIDEO);//因为runOnCallingThread 是 false,也就是这个 start 动作会创建一个新的 Looper 线程
if (nameIsType) {
mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
} else {
mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
以上我们已经创建并启动了一个 looper 的循环,但是此处 looper 还是孤独的,并没有和另外两个组件
建立联系。
以 MediaCodec::CreateByComponentName 为例,这个函数中主要做两件事情。
sp<MediaCodec> codec = new MediaCodec(looper, pid, uid)const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
通过实例化一个 MediaCodec 对象,将 looper 赋值给 codec 的 mLooper 变量。
这里我们已经将一个 looper 和一个 handler (MediaCodec) 联系起来了,但是要真正让这两者工作起
来,这还是不够的, 在 MediaCodec::init 函数中
if (mIsVideo) {
// video codec needs dedicated looper
if (mCodecLooper == NULL) {
mCodecLooper = new ALooper;
mCodecLooper->setName("CodecLooper");
mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
}
mCodecLooper->registerHandler(mCodec);
} else {
// 注册 handler(ACodec)
mLooper->registerHandler(mCodec);
}
// 注册 Handler(MediaCodec)
mLooper->registerHandler(this);
// 给 ACodec 设置一个以 kWhatCodecNotify 为 msg 的 AMessage 变量,这个message 的 handler 是 MediaCodec
mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, this));
sp<AMessage> msg = new AMessage(kWhatInit, this);
msg->setString("name", name);
msg->setInt32("nameIsType", nameIsType);
现在看 ALooper::registerHandler 中是如何做的
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}
gLooperRoster 的类型是 ALooperRoster ,实际上是调用
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.");
return INVALID_OPERATION;
}
HandlerInfo info;
info.mLooper = looper;
info.mHandler = handler;
ALooper::handler_id handlerID = mNextHandlerID++;
//放入KeyedVector中
mHandlers.add(handlerID, info);
//setID 会给 AHandler 的成员变量 mLooper 赋值,记录关联起来的 looper,不过这里的 mLooper 和Mediacodec 中的 mlooper 有点不同,这里的是 wp 类型的,而且 Mediacodec 中的是 sp 类型的。
handler->setID(handlerID, looper);
return handlerID;
}
AMessage 消息发送
以 kWhatInit 的发送和处理为例,在 Mediacodec::init 中,会 new 一个 kWhatInit 的消息,之后
会发送出去,现在看这一段具体是怎么做的。
status_t MediaCodec::PostAndAwaitResponse(const sp<AMessage> &msg, sp<AMessage> *response) {
status_t err = msg->postAndAwaitResponse(response);
if (err != OK) {
return err;
}
if (!(*response)->findInt32("err", &err)) {
err = OK;
}
return err;
}
AMessage 有三种 post 消息的方式,分别是
- post: 把消息发送出去,除此之外什么都不做
- postAndAwaitResponse: 不仅 post 消息,还会等待一个 reply
- postReply:和上一项组合使用,用于发送 reply
status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
sp<ALooper> looper = mLooper.promote();//因为AMessage中持有的 mLooper 是 wp,如果要操作实际对象的话,需要提升到 sp。
if (looper == NULL) {
ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
return -ENOENT;
}
//创建 token
sp<AReplyToken> token = looper->createReplyToken();
if (token == NULL) {
ALOGE("failed to create reply token");
return -ENOMEM;
}
//作为 ID 设置到当前的 msg (kWhatInit)中
setObject("replyID", token);
// post 到 looper中
looper->post(this, 0 /* delayUs */);
return looper->awaitResponse(token, response);//等待回复
}
}
post 消息之后,looper 进入 awaitResponse 开始等待回复。
looper 中的处理
在 looper->post 中,主要干了这些事
Event event;
event.mWhenUs = whenUs;
event.mMessage = msg;
mEventQueue.insert(it, event);
其实就只是将 msg 封装成 event 之后,塞进队列里面。
在 ALooper::start 的时候,会创建一个 LooperThread 来处理 Event 队列,最终是在
ALooper::loop() 中工作的
event = *mEventQueue.begin();
event.mMessage->deliver();
AMessage::deliver()
sp<AHandler> handler = mHandler.promote();//提升到 SP,用于操作对象
handler->deliverMessage(this);
handler 的处理
之后就是 AHandler::deliverMessage()
onMessageReceived(msg);
这时候就到了 MediaCodec 中的函数实现了
case kWhatInit:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
if (mState != UNINITIALIZED) {
// post err
PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
mReplyID = replyID;
//设置当前状态
setState(INITIALIZING);
AString name;
CHECK(msg->findString("name", &name));
int32_t nameIsType;
int32_t encoder = false;
CHECK(msg->findInt32("nameIsType", &nameIsType));
if (nameIsType) {
CHECK(msg->findInt32("encoder", &encoder));
}
sp<AMessage> format = new AMessage;
if (nameIsType) {
format->setString("mime", name.c_str());
format->setInt32("encoder", encoder);
} else {
format->setString("componentName", name.c_str());
}
//调用 ACodec
mCodec->initiateAllocateComponent(format);
break;
}
处理 reply
上面已经进入 MediaCodec 的处理 msg 了,这时需要返回 reply 了,不然那边就还在那里等着。
上面的代码看起来很多,但是并没有真正的发送 reply,而只是把这个消息的 token 通过 mReplyID = replyID; 记录了下来。这个消息的 reply 是在 ACodec 处理完 initiateAllocateComponent 的流程的时
候,通知 MediaCodec 来做的。
bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg)
{ ...
// 这个 notify 就是在之前 mediacodec init 过程中,设置给 ACodec 的,用于
ACodec post 消息给 Mediacodec
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", CodecBase::kWhatComponentAllocated);
notify->setString("componentName", mCodec->mComponentName.c_str());
notify->post();
...
}
然后在
MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatCodecNotify:
{
//...
switch (what) {
case CodecBase::kWhatComponentAllocated:
{
// 切换状态至 INITIALIZED
setState(INITIALIZED);
mFlags |= kFlagIsComponentAllocated;
CHECK(msg->findString("componentName", &mComponentName));
if (mComponentName.startsWith("OMX.google.")) {
mFlags |= kFlagUsesSoftwareRenderer;
} else {
mFlags &= ~kFlagUsesSoftwareRenderer;
}
...
// 发送回复了,把之前记录的 mReplayID 作为参数传了进去
(new AMessage)->postReply(mReplyID);
break;
}
//....其它case
}
}
}
}
AMessage::postReply 最终也是调用 ALooper::postReply
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;
}
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;
}
在把 response 设置给 token 之后,会广播 Condition,唤醒等待中的线程,前文中的
ALooper::awaitResponse 返回
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)) {
mRepliesCondition.wait(mRepliesLock);
}
return OK;
}
bool retrieveReply(sp<AMessage> *reply) {
if (mReplied) {
*reply = mReply;
mReply.clear();
}
return mReplied;
}
这样等待就结束了,MediaCodec 也拿到了相应的返回信息,整个 init 流程也结束了。如果是不带 response 的消息 post ,就更加简单了,只在对应的 handler 里面处理完消息就可以了。
两种不同的 notify
在 Mediacodec-ACodec 中有两种用于 notify 的 AMessage,一种是前文中有提到的,用户 ACodec
post msg to Mediacodec 的,msg name 是 kWhatCodecNotify ,还有一种就是用于 omx post msg
to ACodec 的。
首先在 init 阶段, ACodec::UninitializedState::onAllocateComponent 中会有
sp<CodecObserver> observer = new CodecObserver;
...
notify = new AMessage(kWhatOMXMessageList, mCodec);
observer->setNotificationMessage(notify);
当 OMX 有 EMPTY_BUFFER_DONE, FILL_BUFFER_DONE 等消息通知上层的时候,首先 call
到 CodecObserver::onMessages 中,在这个函数中,会把上报的消息全部都放进列表中
notify->setObject("messages", msgList);
notify->post();
这里的 notify 就是初始化过程中的 kWhatOMXMessageList ,之后就是 ACodec 的
onMessageReceived 函数去处理了。
ACodec 中的 onMessageReceived
如果看代码的话,会注意到在 ACodec.cpp 中其实是没有 ACodec::onMessageReceived 函数对于各
个不同的消息的处理的,反而是在 BaseState, UninitializedState 等类中会有 onMessageReceived。
但这些 State类并不是 AHandler 的子类,所以仍然需要 ACodec::onMessageReceived 。
在 ACodec.h 中,实现了这个函数,内容只有一句
handleMessage(msg);
从 ACodec 的继承关系中可以看到,这个 handleMessage 是 ACodec 的父类 AHierarchicalStateMachine 的函数
void AHierarchicalStateMachine::handleMessage(const sp<AMessage> &msg) {
sp<AState> save = mState;
sp<AState> cur = mState;
// 调用当前状态的 onMessageReceived 函数
while (cur != NULL && !cur->onMessageReceived(msg)) {
// If you claim not to have handled the message you shouldn't
// have called setState...
CHECK(save == mState);
cur = cur->parentState();
}
if (cur != NULL) {
return;
}
ALOGW("Warning message %s unhandled in root state.",
msg->debugString().c_str());
}
总结
梳理了 Mediacodec 和 ACodec 中使用的消息机制,发消息和收消息以及处理消息是如何运作
的,涉及的不同 Class。
需要追踪哪个消息是在哪里处理,看这个消息 new 的时候,设置的 handler 是哪个就行了。
一个线程对应一个 looper,一个 handler 对应一个 looper,而 Handler 和 looper 能处理多个
message。
ACodec 在特定的状态能够处理一些特定的消息,也有一些通用的消息是在所有状态下都能处理
的。