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