您的位置:首页 > 其它

AHandler机制

2015-07-28 16:48 176 查看

AHandler机制

Android APP开发中为了不阻塞UI线程,利用handler,把UI线程分开异步执行,使用handler去执行某项比较费时的操作,然后异步更新UI线程。这部分是用Java来实现的,和传统Java的线程机制很类似。

流媒体(5.0中用的是NuPlayer)中也是类似的,因为联网,codec都很费时,需要异步执行。AHandler机制基于C++的实现,NuPlayer就是继承了AHandler,实际上就是用的AHandler。

对于handler消息机制,构成就必须包括一个Loop,message。那么对应的AHandler,也应该有对应的ALooper, AMessage。底层的多媒体框架NuPlayer中,都是用AHandler消息机制实现的。

结合Nuplayer的构造和媒体播放实现流程中的setDataSource来说明。

####1. NuPlayer构造

首先从NuplayerDriver的构造函数,这是流媒体(NuPlayer)播放初始化函数。代码位置:

[code]frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
  public:
    …
    virtual sp<MediaPlayerBase> createPlayer() {
        ALOGV(" create NuPlayer");
        return new NuPlayerDriver;
    }
};
// NuPlayerDriver中初始化的关键项
NuPlayerDriver::NuPlayerDriver()
    : mState(STATE_IDLE),
      mIsAsyncPrepare(false),
      mAsyncResult(UNKNOWN_ERROR),
      mSetSurfaceInProgress(false),
      mDurationUs(-1),
      mPositionUs(-1),
      mSeekInProgress(false),
      mLooper(new ALooper), // 创建一个 ALooper
      mPlayerFlags(0),
      mAtEOS(false),
      mLooping(false),
      mAutoLoop(false),
      mStartupSeekTimeUs(-1) {
    ALOGV("NuPlayerDriver(%p)", this);
    mLooper->setName("NuPlayerDriver Looper"); // 给该Looper取名字,以便与AHandler一一对应
    mLooper->start( // 启动Looper
            false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO); 

    mPlayer = new NuPlayer; // 创建一个AHandler即Nuplayer
    mLooper->registerHandler(mPlayer); // 把该AHandler注册到Looper中,具体的实现我们往后看
    mPlayer->setDriver(this);
}
// 看到Looper就看成thread,ALooper的启动函数
status_t ALooper::start(
        bool runOnCallingThread, bool canCallJava, int32_t priority) {
if (runOnCallingThread) {
    …
    }
    Mutex::Autolock autoLock(mLock);
    if (mThread != NULL || mRunningLocally) {
        return INVALID_OPERATION;
    }
    mThread = new LooperThread(this, canCallJava); // 创建一个新的thread
    status_t err = mThread->run(
            mName.empty() ? "ALooper" : mName.c_str(), priority); // Thread运行
    if (err != OK) {
        mThread.clear();
    }
    return err;
}
// 注册handler
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
    return gLooperRoster.registerHandler(this, handler);
}
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; // NuPlayerDriver Looper
    info.mHandler = handler; // NuPlayer
    ALooper::handler_id handlerID = mNextHandlerID++;
    mHandlers.add(handlerID, info); // 放进vector里
    handler->setID(handlerID); // 设置handlerID,以便发送message时找到对应的handler
    return handlerID;
}
ALooperRoster::ALooperRoster()
    : mNextHandlerID(1), // 从一开始
      mNextReplyID(1) {
}


gLooperRoster是一个全局变量,并且是一个vector,以handler Id对应HandlerInfo,而HandlerInfo中包含的是一对Looper(就是thread)和handler。这样处理消息的时候,就以handler Id很快地找到对应的处理。

[code]// Nuplayer本身也是个AHandler,因为其继承自AHandler。
struct NuPlayer : public AHandler {
NuPlayer();
…
}
struct AHandler : public RefBase {
    AHandler()
        : mID(0) {
    }
    ALooper::handler_id id() const {
        return mID;
    }
    sp<ALooper> looper();
protected:
    virtual void onMessageReceived(const sp<AMessage> &msg) = 0; // 处理消息
private:
    friend struct ALooperRoster;


有了LOOPER,也有了对应的handler,以setdataSource方法为例,很清晰地看到送消息给LOOPER,交个相应的handler去处理。

2. 以setDataSource看AHandler处理消息机制



[code]void NuPlayer::setDataSourceAsync(
        const sp<IMediaHTTPService> &httpService,
        const char *url,
        const KeyedVector<String8, String8> *headers) {
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); // 创建消息
    size_t len = strlen(url);
    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id()); // 创建消息

    sp<Source> source;
    if (IsHTTPLiveURL(url)) {
        source = new HTTPLiveSource(notify, httpService, url, headers);
    } else if (!strncasecmp(url, "rtsp://", 7)) {
        source = new RTSPSource(
                notify, httpService, url, headers, mUIDValid, mUID);
    } else if ((!strncasecmp(url, "http://", 7)
                || !strncasecmp(url, "https://", 8))
                    && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
                    || strstr(url, ".sdp?"))) {
        source = new RTSPSource(
                notify, httpService, url, headers, mUIDValid, mUID, true);
    } else {
        sp<GenericSource> genericSource =
                new GenericSource(notify, mUIDValid, mUID);
        // Don't set FLAG_SECURE on mSourceFlags here for widevine.
        // The correct flags will be updated in Source::kWhatFlagsChanged
        // handler when  GenericSource is prepared.

        status_t err = genericSource->setDataSource(httpService, url, headers);

        if (err == OK) {
            source = genericSource;
        } else {
            ALOGE("Failed to set data source!");
        }
    }
    msg->setObject("source", source);
    msg->post();
}


NuPlayer中的setDataSource主要做了这些事情:

1 创建相应的消息

2 根据URL创建对应的source

3 onmessageReceive处理对应的消息

首先新建一个AMessage的实例,传入的参数为事件的名称以及处理该消息的Handlerid,该id在mLooper->registerHandler(mPlayer);方法中设置上。

[code]AMessage::AMessage(uint32_t what, ALooper::handler_id target)
    : mWhat(what),
      mTarget(target),
      mNumItems(0) {
}
void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
    setObjectInternal(name, obj, kTypeObject);
}
void AMessage::setObjectInternal(
        const char *name, const sp<RefBase> &obj, Type type) {
    Item *item = allocateItem(name);
    item->mType = type;

    if (obj != NULL) { obj->incStrong(this); }
    item->u.refValue = obj.get();
}
// post过程
void AMessage::post(int64_t delayUs) {
    gLooperRoster.postMessage(this, delayUs);
}
status_t ALooperRoster::postMessage(
        const sp<AMessage> &msg, int64_t delayUs) {
    sp<ALooper> looper = findLooper(msg->target()); // target及为获取handler id
    if (looper == NULL) {
        return -ENOENT;
    }
    looper->post(msg, delayUs);
    return OK;
}
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); // 插入到event queue里
}
// 当队列里有消息时便会触发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());
    }
    gLooperRoster.deliverMessage(event.mMessage); // 处理消息

    // 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 ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
    sp<AHandler> handler;
    {
        Mutex::Autolock autoLock(mLock);
        ssize_t index = mHandlers.indexOfKey(msg->target());
        if (index < 0) {
            ALOGW("failed to deliver message. Target handler not registered.");
            return;
        }
        const HandlerInfo &info = mHandlers.valueAt(index);
        handler = info.mHandler.promote();
        if (handler == NULL) {
            ALOGW("failed to deliver message. "
                 "Target handler %d registered, but object gone.",
                 msg->target());
            mHandlers.removeItemsAt(index);
            return;
        }
    }
    handler->onMessageReceived(msg); // 这里就回去调用NuPlayer
}
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
        case kWhatSetDataSource:
        {
            ALOGV("kWhatSetDataSource");
            CHECK(mSource == NULL);
            status_t err = OK;
            sp<RefBase> obj;
            CHECK(msg->findObject("source", &obj));
            if (obj != NULL) {
                mSource = static_cast<Source *>(obj.get());
            } else {
                err = UNKNOWN_ERROR;
            }
            CHECK(mDriver != NULL);
            sp<NuPlayerDriver> driver = mDriver.promote();
            if (driver != NULL) {
                driver->notifySetDataSourceCompleted(err);
            }
            break;
        }
    }
}


Ahandler就是实现了异步机制,大致就是启动一个threadLooper,监听looper的消息队列是否有变化,如有交给相应的Handler去处理。

NuPlayer底层播放器所有 的实现依赖于这样的AHandler机制转动起来的。Android底层有很多类似的AHandler应用的地方,如在render中、获取m3u8 playlist的地方….
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: