AHandler机制

来源:互联网 发布:python windows 路径 编辑:程序博客网 时间:2024/05/31 19:27

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 class="hljs lasso has-numbering">frameworks/av/media/libmediaplayerservice/MediaPlayerFactory<span class="hljs-built_in">.</span>cppclass NuPlayerFactory : <span class="hljs-keyword">public</span> MediaPlayerFactory<span class="hljs-tag">::IFactory</span> {  <span class="hljs-keyword">public</span>:    …    virtual sp<span class="hljs-subst"><</span>MediaPlayerBase<span class="hljs-subst">></span> createPlayer() {        ALOGV(<span class="hljs-string">" create NuPlayer"</span>);        <span class="hljs-keyword">return</span> <span class="hljs-literal">new</span> NuPlayerDriver;    }};<span class="hljs-comment">// NuPlayerDriver中初始化的关键项</span>NuPlayerDriver<span class="hljs-tag">::NuPlayerDriver</span>()    : mState(STATE_IDLE),      mIsAsyncPrepare(<span class="hljs-literal">false</span>),      mAsyncResult(UNKNOWN_ERROR),      mSetSurfaceInProgress(<span class="hljs-literal">false</span>),      mDurationUs(<span class="hljs-subst">-</span><span class="hljs-number">1</span>),      mPositionUs(<span class="hljs-subst">-</span><span class="hljs-number">1</span>),      mSeekInProgress(<span class="hljs-literal">false</span>),      mLooper(<span class="hljs-literal">new</span> ALooper), <span class="hljs-comment">// 创建一个 ALooper</span>      mPlayerFlags(<span class="hljs-number">0</span>),      mAtEOS(<span class="hljs-literal">false</span>),      mLooping(<span class="hljs-literal">false</span>),      mAutoLoop(<span class="hljs-literal">false</span>),      mStartupSeekTimeUs(<span class="hljs-subst">-</span><span class="hljs-number">1</span>) {    ALOGV(<span class="hljs-string">"NuPlayerDriver(%p)"</span>, this);    mLooper<span class="hljs-subst">-></span>setName(<span class="hljs-string">"NuPlayerDriver Looper"</span>); <span class="hljs-comment">// 给该Looper取名字,以便与AHandler一一对应</span>    mLooper<span class="hljs-subst">-></span>start( <span class="hljs-comment">// 启动Looper</span>            <span class="hljs-literal">false</span>, <span class="hljs-comment">/* runOnCallingThread */</span>            <span class="hljs-literal">true</span>,  <span class="hljs-comment">/* canCallJava */</span>            PRIORITY_AUDIO);     mPlayer <span class="hljs-subst">=</span> <span class="hljs-literal">new</span> NuPlayer; <span class="hljs-comment">// 创建一个AHandler即Nuplayer</span>    mLooper<span class="hljs-subst">-></span>registerHandler(mPlayer); <span class="hljs-comment">// 把该AHandler注册到Looper中,具体的实现我们往后看</span>    mPlayer<span class="hljs-subst">-></span>setDriver(this);}<span class="hljs-comment">// 看到Looper就看成thread,ALooper的启动函数</span>status_t ALooper<span class="hljs-tag">::start</span>(        bool runOnCallingThread, bool canCallJava, int32_t priority) {<span class="hljs-keyword">if</span> (runOnCallingThread) {    …    }    Mutex<span class="hljs-tag">::Autolock</span> autoLock(mLock);    <span class="hljs-keyword">if</span> (mThread <span class="hljs-subst">!=</span> <span class="hljs-built_in">NULL</span> <span class="hljs-subst">||</span> mRunningLocally) {        <span class="hljs-keyword">return</span> INVALID_OPERATION;    }    mThread <span class="hljs-subst">=</span> <span class="hljs-literal">new</span> LooperThread(this, canCallJava); <span class="hljs-comment">// 创建一个新的thread</span>    status_t err <span class="hljs-subst">=</span> mThread<span class="hljs-subst">-></span>run(            mName<span class="hljs-built_in">.</span>empty() <span class="hljs-subst">?</span> <span class="hljs-string">"ALooper"</span> : mName<span class="hljs-built_in">.</span>c_str(), priority); <span class="hljs-comment">// Thread运行</span>    <span class="hljs-keyword">if</span> (err <span class="hljs-subst">!=</span> OK) {        mThread<span class="hljs-built_in">.</span>clear();    }    <span class="hljs-keyword">return</span> err;}<span class="hljs-comment">// 注册handler</span>ALooper<span class="hljs-tag">::handler_id</span> ALooper<span class="hljs-tag">::registerHandler</span>(const sp<span class="hljs-subst"><</span>AHandler<span class="hljs-subst">></span> <span class="hljs-subst">&</span>handler) {    <span class="hljs-keyword">return</span> gLooperRoster<span class="hljs-built_in">.</span>registerHandler(this, handler);}ALooper<span class="hljs-tag">::handler_id</span> ALooperRoster<span class="hljs-tag">::registerHandler</span>(        const sp<span class="hljs-subst"><</span>ALooper<span class="hljs-subst">></span> looper, const sp<span class="hljs-subst"><</span>AHandler<span class="hljs-subst">></span> <span class="hljs-subst">&</span>handler) {    Mutex<span class="hljs-tag">::Autolock</span> autoLock(mLock);    <span class="hljs-keyword">if</span> (handler<span class="hljs-subst">-></span>id() <span class="hljs-subst">!=</span> <span class="hljs-number">0</span>) {        CHECK(<span class="hljs-subst">!</span><span class="hljs-string">"A handler must only be registered once."</span>);        <span class="hljs-keyword">return</span> INVALID_OPERATION;    }    HandlerInfo info;    info<span class="hljs-built_in">.</span>mLooper <span class="hljs-subst">=</span> looper; <span class="hljs-comment">// NuPlayerDriver Looper</span>    info<span class="hljs-built_in">.</span>mHandler <span class="hljs-subst">=</span> handler; <span class="hljs-comment">// NuPlayer</span>    ALooper<span class="hljs-tag">::handler_id</span> handlerID <span class="hljs-subst">=</span> mNextHandlerID<span class="hljs-subst">++</span>;    mHandlers<span class="hljs-built_in">.</span>add(handlerID, info); <span class="hljs-comment">// 放进vector里</span>    handler<span class="hljs-subst">-></span>setID(handlerID); <span class="hljs-comment">// 设置handlerID,以便发送message时找到对应的handler</span>    <span class="hljs-keyword">return</span> handlerID;}ALooperRoster<span class="hljs-tag">::ALooperRoster</span>()    : mNextHandlerID(<span class="hljs-number">1</span>), <span class="hljs-comment">// 从一开始</span>      mNextReplyID(<span class="hljs-number">1</span>) {}</code>

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

<code class="hljs cs has-numbering"><span class="hljs-comment">// Nuplayer本身也是个AHandler,因为其继承自AHandler。</span><span class="hljs-keyword">struct</span> NuPlayer : <span class="hljs-keyword">public</span> AHandler {NuPlayer();…}<span class="hljs-keyword">struct</span> AHandler : <span class="hljs-keyword">public</span> RefBase {    AHandler()        : mID(<span class="hljs-number">0</span>) {    }    ALooper::handler_id id() <span class="hljs-keyword">const</span> {        <span class="hljs-keyword">return</span> mID;    }    sp<ALooper> looper();<span class="hljs-keyword">protected</span>:    <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMessageReceived</span>(<span class="hljs-keyword">const</span> sp<AMessage> &msg) = <span class="hljs-number">0</span>; <span class="hljs-comment">// 处理消息</span><span class="hljs-keyword">private</span>:    friend <span class="hljs-keyword">struct</span> ALooperRoster;</code>

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

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

这里写图片描述

<code class="hljs cpp has-numbering"><span class="hljs-keyword">void</span> NuPlayer::setDataSourceAsync(        <span class="hljs-keyword">const</span> sp<IMediaHTTPService> &httpService,        <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *url,        <span class="hljs-keyword">const</span> KeyedVector<String8, String8> *headers) {    sp<AMessage> msg = <span class="hljs-keyword">new</span> AMessage(kWhatSetDataSource, id()); <span class="hljs-comment">// 创建消息</span>    size_t len = <span class="hljs-built_in">strlen</span>(url);    sp<AMessage> notify = <span class="hljs-keyword">new</span> AMessage(kWhatSourceNotify, id()); <span class="hljs-comment">// 创建消息</span>    sp<Source> source;    <span class="hljs-keyword">if</span> (IsHTTPLiveURL(url)) {        source = <span class="hljs-keyword">new</span> HTTPLiveSource(notify, httpService, url, headers);    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!strncasecmp(url, <span class="hljs-string">"rtsp://"</span>, <span class="hljs-number">7</span>)) {        source = <span class="hljs-keyword">new</span> RTSPSource(                notify, httpService, url, headers, mUIDValid, mUID);    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((!strncasecmp(url, <span class="hljs-string">"http://"</span>, <span class="hljs-number">7</span>)                || !strncasecmp(url, <span class="hljs-string">"https://"</span>, <span class="hljs-number">8</span>))                    && ((len >= <span class="hljs-number">4</span> && !strcasecmp(<span class="hljs-string">".sdp"</span>, &url[len - <span class="hljs-number">4</span>]))                    || <span class="hljs-built_in">strstr</span>(url, <span class="hljs-string">".sdp?"</span>))) {        source = <span class="hljs-keyword">new</span> RTSPSource(                notify, httpService, url, headers, mUIDValid, mUID, <span class="hljs-keyword">true</span>);    } <span class="hljs-keyword">else</span> {        sp<GenericSource> genericSource =                <span class="hljs-keyword">new</span> GenericSource(notify, mUIDValid, mUID);        <span class="hljs-comment">// Don't set FLAG_SECURE on mSourceFlags here for widevine.</span>        <span class="hljs-comment">// The correct flags will be updated in Source::kWhatFlagsChanged</span>        <span class="hljs-comment">// handler when  GenericSource is prepared.</span>        status_t err = genericSource->setDataSource(httpService, url, headers);        <span class="hljs-keyword">if</span> (err == OK) {            source = genericSource;        } <span class="hljs-keyword">else</span> {            ALOGE(<span class="hljs-string">"Failed to set data source!"</span>);        }    }    msg->setObject(<span class="hljs-string">"source"</span>, source);    msg->post();}</code>

NuPlayer中的setDataSource主要做了这些事情:
1 创建相应的消息
2 根据URL创建对应的source
3 onmessageReceive处理对应的消息

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

<code class="hljs lasso has-numbering">AMessage<span class="hljs-tag">::AMessage</span>(uint32_t what, ALooper<span class="hljs-tag">::handler_id</span> target)    : mWhat(what),      mTarget(target),      mNumItems(<span class="hljs-number">0</span>) {}<span class="hljs-literal">void</span> AMessage<span class="hljs-tag">::setObject</span>(const char <span class="hljs-subst">*</span>name, const sp<span class="hljs-subst"><</span>RefBase<span class="hljs-subst">></span> <span class="hljs-subst">&</span>obj) {    setObjectInternal(name, obj, kTypeObject);}<span class="hljs-literal">void</span> AMessage<span class="hljs-tag">::setObjectInternal</span>(        const char <span class="hljs-subst">*</span>name, const sp<span class="hljs-subst"><</span>RefBase<span class="hljs-subst">></span> <span class="hljs-subst">&</span>obj, <span class="hljs-keyword">Type</span> <span class="hljs-keyword">type</span>) {    Item <span class="hljs-subst">*</span>item <span class="hljs-subst">=</span> allocateItem(name);    item<span class="hljs-subst">-></span>mType <span class="hljs-subst">=</span> <span class="hljs-keyword">type</span>;    <span class="hljs-keyword">if</span> (obj <span class="hljs-subst">!=</span> <span class="hljs-built_in">NULL</span>) { obj<span class="hljs-subst">-></span>incStrong(this); }    item<span class="hljs-subst">-></span>u<span class="hljs-built_in">.</span>refValue <span class="hljs-subst">=</span> obj<span class="hljs-built_in">.</span>get();}<span class="hljs-comment">// post过程</span><span class="hljs-literal">void</span> AMessage<span class="hljs-tag">::post</span>(int64_t delayUs) {    gLooperRoster<span class="hljs-built_in">.</span>postMessage(this, delayUs);}status_t ALooperRoster<span class="hljs-tag">::postMessage</span>(        const sp<span class="hljs-subst"><</span>AMessage<span class="hljs-subst">></span> <span class="hljs-subst">&</span>msg, int64_t delayUs) {    sp<span class="hljs-subst"><</span>ALooper<span class="hljs-subst">></span> looper <span class="hljs-subst">=</span> findLooper(msg<span class="hljs-subst">-></span>target()); <span class="hljs-comment">// target及为获取handler id</span>    <span class="hljs-keyword">if</span> (looper <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) {        <span class="hljs-keyword">return</span> <span class="hljs-attribute">-ENOENT</span>;    }    looper<span class="hljs-subst">-></span>post(msg, delayUs);    <span class="hljs-keyword">return</span> OK;}<span class="hljs-literal">void</span> ALooper<span class="hljs-tag">::post</span>(const sp<span class="hljs-subst"><</span>AMessage<span class="hljs-subst">></span> <span class="hljs-subst">&</span>msg, int64_t delayUs) {    Mutex<span class="hljs-tag">::Autolock</span> autoLock(mLock);    int64_t whenUs;    <span class="hljs-keyword">if</span> (delayUs <span class="hljs-subst">></span> <span class="hljs-number">0</span>) {        whenUs <span class="hljs-subst">=</span> GetNowUs() <span class="hljs-subst">+</span> delayUs;    } <span class="hljs-keyword">else</span> {        whenUs <span class="hljs-subst">=</span> GetNowUs();    }    <span class="hljs-built_in">List</span><span class="hljs-subst"><</span>Event<span class="hljs-subst">></span><span class="hljs-tag">::iterator</span> it <span class="hljs-subst">=</span> mEventQueue<span class="hljs-built_in">.</span>begin();    <span class="hljs-keyword">while</span> (it <span class="hljs-subst">!=</span> mEventQueue<span class="hljs-built_in">.</span>end() <span class="hljs-subst">&&</span> (<span class="hljs-subst">*</span>it)<span class="hljs-built_in">.</span>mWhenUs <span class="hljs-subst"><=</span> whenUs) {        <span class="hljs-subst">++</span>it;    }    Event event;    event<span class="hljs-built_in">.</span>mWhenUs <span class="hljs-subst">=</span> whenUs;    event<span class="hljs-built_in">.</span>mMessage <span class="hljs-subst">=</span> msg;    <span class="hljs-keyword">if</span> (it <span class="hljs-subst">==</span> mEventQueue<span class="hljs-built_in">.</span>begin()) {        mQueueChangedCondition<span class="hljs-built_in">.</span>signal();    }    mEventQueue<span class="hljs-built_in">.</span>insert(it, event); <span class="hljs-comment">// 插入到event queue里</span>}<span class="hljs-comment">// 当队列里有消息时便会触发loop函数:</span>bool ALooper<span class="hljs-tag">::loop</span>() {    Event event;    {        Mutex<span class="hljs-tag">::Autolock</span> autoLock(mLock);        <span class="hljs-keyword">if</span> (mThread <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span> <span class="hljs-subst">&&</span> <span class="hljs-subst">!</span>mRunningLocally) {            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;        }        <span class="hljs-keyword">if</span> (mEventQueue<span class="hljs-built_in">.</span>empty()) {            mQueueChangedCondition<span class="hljs-built_in">.</span>wait(mLock);            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;        }        int64_t whenUs <span class="hljs-subst">=</span> (<span class="hljs-subst">*</span>mEventQueue<span class="hljs-built_in">.</span>begin())<span class="hljs-built_in">.</span>mWhenUs;        int64_t nowUs <span class="hljs-subst">=</span> GetNowUs();        <span class="hljs-keyword">if</span> (whenUs <span class="hljs-subst">></span> nowUs) {            int64_t delayUs <span class="hljs-subst">=</span> whenUs <span class="hljs-subst">-</span> nowUs;            mQueueChangedCondition<span class="hljs-built_in">.</span>waitRelative(mLock, delayUs <span class="hljs-subst">*</span> <span class="hljs-number">1000</span>ll);            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;        }        event <span class="hljs-subst">=</span> <span class="hljs-subst">*</span>mEventQueue<span class="hljs-built_in">.</span>begin();        mEventQueue<span class="hljs-built_in">.</span>erase(mEventQueue<span class="hljs-built_in">.</span>begin());    }    gLooperRoster<span class="hljs-built_in">.</span>deliverMessage(event<span class="hljs-built_in">.</span>mMessage); <span class="hljs-comment">// 处理消息</span>    <span class="hljs-comment">// NOTE: It's important to note that at this point our "ALooper" object</span>    <span class="hljs-comment">// may no longer exist (its final reference may have gone away while</span>    <span class="hljs-comment">// delivering the message). We have made sure, however, that loop()</span>    <span class="hljs-comment">// won't be called again.</span>    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;}<span class="hljs-literal">void</span> ALooperRoster<span class="hljs-tag">::deliverMessage</span>(const sp<span class="hljs-subst"><</span>AMessage<span class="hljs-subst">></span> <span class="hljs-subst">&</span>msg) {    sp<span class="hljs-subst"><</span>AHandler<span class="hljs-subst">></span> handler;    {        Mutex<span class="hljs-tag">::Autolock</span> autoLock(mLock);        ssize_t index <span class="hljs-subst">=</span> mHandlers<span class="hljs-built_in">.</span>indexOfKey(msg<span class="hljs-subst">-></span>target());        <span class="hljs-keyword">if</span> (index <span class="hljs-subst"><</span> <span class="hljs-number">0</span>) {            ALOGW(<span class="hljs-string">"failed to deliver message. Target handler not registered."</span>);            <span class="hljs-keyword">return</span>;        }        const HandlerInfo <span class="hljs-subst">&</span>info <span class="hljs-subst">=</span> mHandlers<span class="hljs-built_in">.</span>valueAt(index);        handler <span class="hljs-subst">=</span> info<span class="hljs-built_in">.</span>mHandler<span class="hljs-built_in">.</span>promote();        <span class="hljs-keyword">if</span> (handler <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) {            ALOGW(<span class="hljs-string">"failed to deliver message. "</span>                 <span class="hljs-string">"Target handler %d registered, but object gone."</span>,                 msg<span class="hljs-subst">-></span>target());            mHandlers<span class="hljs-built_in">.</span>removeItemsAt(index);            <span class="hljs-keyword">return</span>;        }    }    handler<span class="hljs-subst">-></span>onMessageReceived(msg); <span class="hljs-comment">// 这里就回去调用NuPlayer</span>}<span class="hljs-literal">void</span> NuPlayer<span class="hljs-tag">::onMessageReceived</span>(const sp<span class="hljs-subst"><</span>AMessage<span class="hljs-subst">></span> <span class="hljs-subst">&</span>msg) {switch (msg<span class="hljs-subst">-></span>what()) {        <span class="hljs-keyword">case</span> kWhatSetDataSource:        {            ALOGV(<span class="hljs-string">"kWhatSetDataSource"</span>);            CHECK(mSource <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>);            status_t err <span class="hljs-subst">=</span> OK;            sp<span class="hljs-subst"><</span>RefBase<span class="hljs-subst">></span> obj;            CHECK(msg<span class="hljs-subst">-></span>findObject(<span class="hljs-string">"source"</span>, <span class="hljs-subst">&</span>obj));            <span class="hljs-keyword">if</span> (obj <span class="hljs-subst">!=</span> <span class="hljs-built_in">NULL</span>) {                mSource <span class="hljs-subst">=</span> static_cast<span class="hljs-subst"><</span>Source <span class="hljs-subst">*></span>(obj<span class="hljs-built_in">.</span>get());            } <span class="hljs-keyword">else</span> {                err <span class="hljs-subst">=</span> UNKNOWN_ERROR;            }            CHECK(mDriver <span class="hljs-subst">!=</span> <span class="hljs-built_in">NULL</span>);            sp<span class="hljs-subst"><</span>NuPlayerDriver<span class="hljs-subst">></span> driver <span class="hljs-subst">=</span> mDriver<span class="hljs-built_in">.</span>promote();            <span class="hljs-keyword">if</span> (driver <span class="hljs-subst">!=</span> <span class="hljs-built_in">NULL</span>) {                driver<span class="hljs-subst">-></span>notifySetDataSourceCompleted(err);            }            break;        }    }}</code>

Ahandler就是实现了异步机制,大致就是启动一个threadLooper,监听looper的消息队列是否有变化,如有交给相应的Handler去处理。
NuPlayer底层播放器所有 的实现依赖于这样的AHandler机制转动起来的。Android底层有很多类似的AHandler应用的地方,如在render中、获取m3u8 playlist的地方….

0 0