美文网首页
Android多媒体框架--04:ALooper-AHandle

Android多媒体框架--04:ALooper-AHandle

作者: DarcyZhou | 来源:发表于2023-04-18 08:20 被阅读0次

"本文转载自:[yanbixing123]的Android MultiMedia框架完全解析 - ALooper-AHandler-AMessage机制分析"

1.概述

  NuPlayer基于Stagefright的基础类构建,使用了Android native层的ALooper-AHandler-AMessage机制来异步处理消息。在这个机制中所有的处理都是异步的,将变量封装到一个消息AMessage结构体中,然后放到队列中去,后台专门有一个线程会从这个队列中取出消息然后执行,执行函数就是onMessageReceived,在NuPlayer中就是使用了该消息机制。

image.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操作执行完毕后,直接把画面切换到这个位置开始播放,这样的话,画面不会卡住,而用户体验度就会好很多。

相关文章

网友评论

      本文标题:Android多媒体框架--04:ALooper-AHandle

      本文链接:https://www.haomeiwen.com/subject/nrjbddtx.html