"本文转载自:[yanbixing123]的Android MultiMedia框架完全解析 - ALooper-AHandler-AMessage机制分析"
1.概述
NuPlayer基于Stagefright的基础类构建,使用了Android native层的ALooper-AHandler-AMessage机制来异步处理消息。在这个机制中所有的处理都是异步的,将变量封装到一个消息AMessage结构体中,然后放到队列中去,后台专门有一个线程会从这个队列中取出消息然后执行,执行函数就是onMessageReceived,在NuPlayer中就是使用了该消息机制。
![](https://img.haomeiwen.com/i18565088/b30f9929463b4da8.png)
-
AMessage:消息载体,用于构造消息,通过post方法投递出去给ALooper;
-
AHandler:消息处理类,一般当做父类,继承该类的子类需要实现onMessageReceived方法,它是最终对消息进行处理的;
-
ALooper:它运行着一个后台线程,来循环处理接收到的消息。与AHander一一对应,负责存储消息并分发AHandler的消息,与AMessage一对多关系;
-
LooperThread:此线程调用ALooper的loop方法来分发消息;
-
ALooperRoaster:与Handler是一对多的关系, 管理Looper和Handler一一对应关系,负责释放stale handler。
2.AHandler
首先需要分析AHandler,因为在Amessage::setTarget函数中,需要使用AHandler中的const_cast<AHandler *>(this)和mLooper,而这两个值需要在AHandler中初始化。
先来看AHandler的定义:frameworks/av/include/media/stagefright/foundation/AHandler.h
struct AHandler : public RefBase {
AHandler()
: mID(0),
mVerboseStats(false),
mMessageCounter(0) {
}//构造函数,为mID,mVerboseStats,mMessageCounter三个变量赋初始值。
ALooper::handler_id id() const {
return mID;
}
sp<ALooper> looper() const {
return mLooper.promote();
}
wp<ALooper> getLooper() const {
return mLooper;
}
wp<AHandler> getHandler() const {
// allow getting a weak reference to a const handler
return const_cast<AHandler *>(this);
}
protected:
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
//这是个虚函数,每个继承自AHandler的子类都需要实现这个虚函数,比如在NuPlayer中就实现了这个虚函数,最终对于Message的处理还是在这个函数中。
private:
friend struct AMessage; // deliverMessage()
friend struct ALooperRoster; // setID()
ALooper::handler_id mID;
wp<ALooper> mLooper;
inline void setID(ALooper::handler_id id, wp<ALooper> looper) {
mID = id;
mLooper = looper;
}//这个函数供友元类ALooperRoster来使用。
bool mVerboseStats;
uint32_t mMessageCounter;
KeyedVector<uint32_t, uint32_t> mMessages;
//这里是一个KeyedVector,用来存放Messages,它是一个键值对类型的容器。
void deliverMessage(const sp<AMessage> &msg);
//这个函数是AHandler.cpp需要去实现的一个函数,在里面调用onMessageReceived(msg)函数后再对mMessageCounter进行操作。这个函数是在AMessage::deliver()函数中调用的。
DISALLOW_EVIL_CONSTRUCTORS(AHandler);
};
AHandler没有直接对外的接口(只有获取成员变量的接口),基本上只有一个onMessageReceived用于子类的继承,deliverMessage用于给类AMessage使用,setID用于给友元类ALooperRoster来使用。所以从这点来说,真正的代码可能在AMessage中。
那这个AHandler是给谁用的呢?从名字上来看,它是一个句柄,同时也是这个ALooper-AHandler-AMessage机制的一个核心,所以如果想要在某个程序中使用这个消息机制,就要让主体继承自这个AHandler,然后在主体中实现onMessageReceived函数,同时也就可以使用AMessage了。比如在Media playback中,这个主体就是NuPlayer,所以NuPlayer继承自AHanlder。
3.ALooperRoster
从AHandler中setID函数的实现上面来看,是为AHandler中的的mID,mLooper两个成员赋初始值,而调用这个setID函数的正是ALooperRoster::registerHandler()和ALooperRoster::unregisterHandler()这两个函数,同时Roster的翻译就是花名册,所以,可以理解为ALooperRoster类来将ALooper和AHandler类关联起来,它是用来管理ALooper的。
- ALooperRoster.cpp
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++;
mHandlers.add(handlerID, info);
handler->setID(handlerID, looper);
return handlerID;
}
4.AMessage
- AMessage.h
struct AMessage : public RefBase {
AMessage();
AMessage(uint32_t what, const sp<const AHandler> &handler);
//两个构造函数,但是第二个是常用的构造函数,通常指定id和需要哪个AHanlder来处理。
static sp<AMessage> FromParcel(const Parcel &parcel);
void writeToParcel(Parcel *parcel) const;
void setWhat(uint32_t what);
uint32_t what() const;
//如果不通过构造函数传入id的话,也可以通过这两个设置。
void setTarget(const sp<const AHandler> &handler);
//这个函数还是蛮重要的,通过这个函数,可以为这个类中的mTarget,mHandler,mLooper赋值,这个函数通常会被这样调用:msg->setTarget(this);通过这一步,就可以为msg设置Handler和Looper。注意这里本身传入的就是一个AHandler。
void clear();
void setInt32(const char *name, int32_t value);
void setInt64(const char *name, int64_t value);
void setSize(const char *name, size_t value);
void setFloat(const char *name, float value);
void setDouble(const char *name, double value);
void setPointer(const char *name, void *value);
void setString(const char *name, const char *s, ssize_t len = -1);
void setString(const char *name, const AString &s);
void setObject(const char *name, const sp<RefBase> &obj);
void setBuffer(const char *name, const sp<ABuffer> &buffer);
void setMessage(const char *name, const sp<AMessage> &obj);
void setRect(
const char *name,
int32_t left, int32_t top, int32_t right, int32_t bottom);
bool contains(const char *name) const;
bool findInt32(const char *name, int32_t *value) const;
bool findInt64(const char *name, int64_t *value) const;
bool findSize(const char *name, size_t *value) const;
bool findFloat(const char *name, float *value) const;
bool findDouble(const char *name, double *value) const;
bool findPointer(const char *name, void **value) const;
bool findString(const char *name, AString *value) const;
bool findObject(const char *name, sp<RefBase> *obj) const;
bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
bool findMessage(const char *name, sp<AMessage> *obj) const;
bool findRect(
const char *name,
int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
status_t post(int64_t delayUs = 0);
//投递消息,新建一个消息后,设置属性,就可以调用post投递了。
status_t postAndAwaitResponse(sp<AMessage> *response);
bool senderAwaitsResponse(sp<AReplyToken> *replyID);
status_t postReply(const sp<AReplyToken> &replyID);
sp<AMessage> dup() const;
size_t countEntries() const;
const char *getEntryNameAt(size_t index, Type *type) const;
protected:
virtual ~AMessage();
private:
friend struct ALooper; // deliver()
uint32_t mWhat;
// used only for debugging
ALooper::handler_id mTarget;
wp<AHandler> mHandler;
wp<ALooper> mLooper;
Item *allocateItem(const char *name);
void freeItemValue(Item *item);
const Item *findItem(const char *name, Type type) const;
void setObjectInternal(
const char *name, const sp<RefBase> &obj, Type type);
size_t findItemIndex(const char *name, size_t len) const;
void deliver();//这个接口给ALooper调用,发送消息的接口。
DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};
从上面的接口可以看出,在使用AMessage时只需要指定消息的id和要处理该消息的AHandler即可,可以通过AMessage的构造函数中指定,也可以单独调用setWhat和setTarget接口。AMessage构造完成之后,可以调用setxxx设置对应的参数,通过findxxx获取传递的参数。最后通过post即可将消息投递到AHandler的消息队列中。
下面就是一个消息从创建到发送的过程:
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatPrepared);
notify->setInt32("err", err);
notify->post();
5.ALooper
- ALooper.h
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);
};
ALooper的对外接口比较简单,通常就是NuPlayerDriver构造函数中的调用逻辑,先创建一个ALooper对象,然后调用setName和start接口,之后调用registerHandler设置一个Handler,这样就完成了初始化。
(1)创建过程
mLooper(new ALooper),
mLooper->setName("NuPlayerDriver Looper");
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
mLooper->registerHandler(mPlayer);
(2)在ALooper::start函数中会启动一个线程,并调用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;
}
1)ALooper中维护着一个Event链表,List<Event> mEventQueue;这个Event结构体中包含一个AMessage消息和一个时间值,在ALooper::loop()函数中会一直监听着这个链表,如果有一个Event的话,就从链表中取出Event,调用event.mMessage->deliver()函数。
2)在AMessage::deliver()函数中,就会去获取这个AMessage的AHandler,然后调用对应AHandler的deliverMessage函数:handler->deliverMessage(this)。
3)在AHandler::deliverMessage(const sp<AMessage> &msg)函数中,就会调用AHandler中的onMessageReceived(msg)函数,如果这时候传入的是AHandler子类的话,就会去调用AHandler子类的onMessageReceived(msg)函数。
6.代码实例
(1)
void NuPlayer::setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer) {
sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
if (bufferProducer == NULL) {
msg->setObject("surface", NULL);
} else {
msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
}
msg->post();
}
上面这些消息都是异步实现的,如果想要同步消息,在这个消息机制中的设计很巧妙,它是新建一个AMessage,然后通过这个新建的AMessage来传输回复的消息,实例如下:
status_t NuPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
sp<AMessage> response;
status_t err = msg->postAndAwaitResponse(&response);
if (err == OK && response != NULL) {
CHECK(response->findInt32("err", &err));
if (err == OK) {
readFromAMessage(response, rate);
}
}
return err;
}
(2)
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
(3)
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
response->postReply(replyID);
(4) msg中findObject和setObject的用法:
void NuPlayer::setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer) {
sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
if (bufferProducer == NULL) {
msg->setObject("surface", NULL);
} else {
msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
}
msg->post();
}
(5)之后在OnMessageReceived函数中:
case kWhatSetVideoSurface:
{
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
sp<Surface> surface = static_cast<Surface *>(obj.get());
这里通过一个static_cast转换,就能够把这个sp<Surface>结构体通过msg传递过来。
7.小结
从上面的分析过程可知,ALooper-AHandler-AMessag异步消息机制使用和实现都很复杂,但是NuPlayer中还是使用了该消息机制。主要原因是NuPlayer多媒体架构中有很多耗时较长的操作,但是用户对于画面的流程度的容忍度很低,举一个很简单的例子,在视频的播放过程中,如果直接把进度条拖到中间的位置开始播放,当点击完毕后,NuPlayer内部就开始执行了seek操作,但是这时候耗时比较长,画面就卡在这里了,用户体验度很差。但是如果使用类似的机制,画面继续播放之前缓冲的数据,而NuPlayer抓紧去执行seek操作,当seek操作执行完毕后,直接把画面切换到这个位置开始播放,这样的话,画面不会卡住,而用户体验度就会好很多。
网友评论