AHandler机制

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>cpp
class 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的地方….

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值