Android CarAudioFocus详解(二)

编程入门 行业动态 更新时间:2024-10-09 06:26:40

Android CarAudioFocus<a href=https://www.elefans.com/category/jswz/34/1770044.html style=详解(二)"/>

Android CarAudioFocus详解(二)

前面文章分析了申请调用的流程,同时也有一篇执行流程的文字描述,但是总觉得不够清晰,让我们来用图示和代码结合说下这部分:

AudioRequester:AudioFocusInfo为传入的参数,里面包含了USAGE类型,GAIN类型,ClientId。其中permanent表示是否永久获取焦点,allowDucking表示是否接受duck。

mFocusHolders:焦点持有者HashMap。里面保存了当前持有焦点的FocusEntry。

mFocusLosers:也是HashMap,里面保存了被抢占焦点,有可能恢复持有的FocusEntry。

losers:AudioRequester和mFocusHolders通过evaluateRequest函数冲突后生成的中间产物。

blocks:AudioRequester和mFocusLosers通过evaluateRequest函数冲突后生成的中间产物。

mDelayerRequest:保存未申请成功,但是接受延迟获得焦点的FocusEntry。

介绍完成员开始介绍申请焦点的流程:

流程一:mFocusHolders和和AudioRequester的冲突:

        Log.i(TAG, "Scanning focus holders...");final ArrayList<FocusEntry> losers = new ArrayList<FocusEntry>();for (FocusEntry entry : mFocusHolders.values()) {Log.d(TAG, "Evaluating focus holder: " + entry.getClientId());// If this request is for Notifications and a current focus holder has specified// AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE, then reject the request.// This matches the hardwired behavior in the default audio policy engine which apps// might expect (The interaction matrix doesn't have any provision for dealing with// override flags like this).if ((requestedContext == CarAudioContext.NOTIFICATION)&& (entry.getAudioFocusInfo().getGainRequest()== AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {return AudioManager.AUDIOFOCUS_REQUEST_FAILED;}// We don't allow sharing listeners (client IDs) between two concurrent requests// (because the app would have no way to know to which request a later event applied)if (afi.getClientId().equals(entry.getAudioFocusInfo().getClientId())) {if (entry.getAudioContext() == requestedContext) {// This is a request from a current focus holder.// Abandon the previous request (without sending a LOSS notification to it),// and don't check the interaction matrix for it.Log.i(TAG, "Replacing accepted request from same client");replacedCurrentEntry = entry;continue;} else {// Trivially reject a request for a different USAGELog.e(TAG, String.format("Client %s has already requested focus for %s - cannot request focus "+ "for %s on same listener.",entry.getClientId(),entry.getAudioFocusInfo().getAttributes().usageToString(),afi.getAttributes().usageToString()));return AudioManager.AUDIOFOCUS_REQUEST_FAILED;}}@AudioManager.FocusRequestResult int interactionResult = mFocusInteraction.evaluateRequest(requestedContext, entry, losers, allowDucking,allowDelayedFocus);if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {return interactionResult;}if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {delayFocusForCurrentRequest = true;}}

流程二:mFocusLosers和和AudioRequester的冲突:

Log.i(TAG, "Scanning those who've already lost focus...");final ArrayList<FocusEntry> blocked = new ArrayList<FocusEntry>();for (FocusEntry entry : mFocusLosers.values()) {Log.i(TAG, entry.getAudioFocusInfo().getClientId());// If this request is for Notifications and a pending focus holder has specified// AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE, then reject the requestif ((requestedContext == CarAudioContext.NOTIFICATION)&& (entry.getAudioFocusInfo().getGainRequest()== AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {return AudioManager.AUDIOFOCUS_REQUEST_FAILED;}// We don't allow sharing listeners (client IDs) between two concurrent requests// (because the app would have no way to know to which request a later event applied)if (afi.getClientId().equals(entry.getAudioFocusInfo().getClientId())) {if (entry.getAudioContext() == requestedContext) {// This is a repeat of a request that is currently blocked.// Evaluate it as if it were a new request, but note that we should remove// the old pending request, and move it.// We do not want to evaluate the new request against itself.Log.i(TAG, "Replacing pending request from same client");replacedBlockedEntry = entry;continue;} else {// Trivially reject a request for a different USAGELog.e(TAG, String.format("Client %s has already requested focus for %s - cannot request focus "+ "for %s on same listener.",entry.getClientId(),entry.getAudioFocusInfo().getAttributes().usageToString(),afi.getAttributes().usageToString()));return AudioManager.AUDIOFOCUS_REQUEST_FAILED;}}@AudioManager.FocusRequestResult int interactionResult = mFocusInteraction.evaluateRequest(requestedContext, entry, blocked, allowDucking,allowDelayedFocus);if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {return interactionResult;}if (interactionResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {delayFocusForCurrentRequest = true;}}

流程三:mFocusInteraction.evaluateRequest执行冲突:

public @FocusRequestResult int evaluateRequest(@AudioContext int requestedContext,FocusEntry focusHolder, List<FocusEntry> focusLosers, boolean allowDucking,boolean allowsDelayedFocus) {@AudioContext int holderContext = focusHolder.getAudioContext();Preconditions.checkArgumentInRange(holderContext, 0, mInteractionMatrix.length - 1,"holderContext");synchronized (mLock) {int[] holderRow = mInteractionMatrix[holderContext];Preconditions.checkArgumentInRange(requestedContext, 0, holderRow.length - 1,"requestedContext");switch (holderRow[requestedContext]) {case INTERACTION_REJECT:if (allowsDelayedFocus) {return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;}return AudioManager.AUDIOFOCUS_REQUEST_FAILED;case INTERACTION_EXCLUSIVE:focusLosers.add(focusHolder);return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;case INTERACTION_CONCURRENT:// If ducking isn't allowed by the focus requester, then everybody else// must get a LOSS.// If a focus holder has set the AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS flag,// they must get a LOSS message even if ducking would otherwise be allowed.// If a focus holder holds the RECEIVE_CAR_AUDIO_DUCKING_EVENTS permission,// they must receive all audio focus losses.if (!allowDucking|| focusHolder.wantsPauseInsteadOfDucking()|| focusHolder.receivesDuckEvents()) {focusLosers.add(focusHolder);}return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;default:Log.e(TAG, String.format("Unsupported CarAudioContext %d - rejecting request",holderRow[requestedContext]));return AudioManager.AUDIOFOCUS_REQUEST_FAILED;}}}

流程四:blocked处理

for (FocusEntry entry : blocked) {// If we're out of focus it must be because somebody is blocking usassert !entry.isUnblocked();if (permanent) {// This entry has now lost focus foreversendFocusLossLocked(entry.getAudioFocusInfo(), AudioManager.AUDIOFOCUS_LOSS);entry.setDucked(false);final FocusEntry deadEntry = mFocusLosers.remove(entry.getAudioFocusInfo().getClientId());assert deadEntry != null;permanentlyLost.add(entry);} else {if (!allowDucking && entry.isDucked()) {// This entry was previously allowed to duck, but can no longer do so.Log.i(TAG, "Converting duckable loss to non-duckable for "+ entry.getClientId());sendFocusLossLocked(entry.getAudioFocusInfo(),AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);entry.setDucked(false);}// Note that this new request is yet one more reason we can't (yet) have focusentry.addBlocker(newEntry);}}

流程五:losers的处理:

for (FocusEntry entry : losers) {// If we have focus (but are about to loose it), nobody should be blocking us yetassert entry.isUnblocked();int lossType;if (permanent) {lossType = AudioManager.AUDIOFOCUS_LOSS;} else if (allowDucking && entry.receivesDuckEvents()) {lossType = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;entry.setDucked(true);} else {lossType = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;}sendFocusLossLocked(entry.getAudioFocusInfo(), lossType);// The entry no longer holds focus, so take it out of the holders listmFocusHolders.remove(entry.getAudioFocusInfo().getClientId());if (permanent) {permanentlyLost.add(entry);} else {// Add ourselves to the list of requests waiting to get focus back and// note why we lost focus so we can tell when it's time to get it backmFocusLosers.put(entry.getAudioFocusInfo().getClientId(), entry);entry.addBlocker(newEntry);}}

流程六:对于永久失去焦点的permanentlyLost这个HashMap进行处理:

        for (FocusEntry entry : permanentlyLost) {Log.d(TAG, "Cleaning up entry " + entry.getClientId());removeBlockerAndRestoreUnblockedWaitersLocked(entry);}

流程七:对于AudioRequester接受延迟获得焦点的处理:

        if (delayFocusForCurrentRequest) {Log.e(TAG,"delayFocusForCurrentRequest");swapDelayedAudioFocusRequestLocked(afi);return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;}private void swapDelayedAudioFocusRequestLocked(AudioFocusInfo afi) {// If we are swapping to a different client then send the focus loss signalif (mDelayedRequest != null&& !afi.getClientId().equals(mDelayedRequest.getClientId())) {sendFocusLossLocked(mDelayedRequest, AudioManager.AUDIOFOCUS_LOSS);}mDelayedRequest = afi;}

总图如下:

接下来是释放焦点的流程:

流程一:从mFocusHloders和mFocusLoser中分别尝试删除AudioReleaser

private FocusEntry removeFocusEntryLocked(AudioFocusInfo afi) {Log.i(TAG, "removeFocusEntry " + afi.getClientId());// Remove this entry from our active or pending listFocusEntry deadEntry = mFocusHolders.remove(afi.getClientId());if (deadEntry == null) {deadEntry = mFocusLosers.remove(afi.getClientId());if (deadEntry == null) {// Caller is providing an unrecognzied clientId!?Log.w(TAG, "Audio focus abandoned by unrecognized client id: " + afi.getClientId());// This probably means an app double released focused for some reason.  One// harmless possibility is a race between an app being told it lost focus and the// app voluntarily abandoning focus.  More likely the app is just sloppy.  :)// The more nefarious possibility is that the clientId is actually corrupted// somehow, in which case we might have a real focus entry that we're going to fail// to remove. If that were to happen, I'd expect either the app to swallow it// silently, or else take unexpected action (eg: resume playing spontaneously), or// else to see "Failure to signal ..." gain/loss error messages in the log from// this module when a focus change tries to take action on a truly zombie entry.}}return deadEntry;}

流程二:如果从mFocusHolders或mFocusLosers中找到了要删除的FocusEntry,就让mDelayedRequest走一遍evaluateFocusRequestLocked冲突函数,如果冲突结果是AUDIOFOCUS_REQUEST_GRANTED,那mDelayedRequest就成了焦点持有者:

代码一:private void removeBlockerAndRestoreUnblockedWaitersLocked(FocusEntry deadEntry) {attemptToGainFocusForDelayedAudioFocusRequest();removeBlockerAndRestoreUnblockedFocusLosersLocked(deadEntry);}
代码二:private void attemptToGainFocusForDelayedAudioFocusRequest() {if (!mEnabledDelayedFocusRequest || mDelayedRequest == null) {return;}int delayedFocusRequestResults = evaluateFocusRequestLocked(mDelayedRequest);if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {FocusEntry focusEntry = mFocusHolders.get(mDelayedRequest.getClientId());mDelayedRequest = null;if (dispatchFocusGainedLocked(focusEntry.getAudioFocusInfo())== AudioManager.AUDIOFOCUS_REQUEST_FAILED) {Log.e(TAG,"Failure to signal gain of audio focus gain for "+ "delayed focus clientId " + focusEntry.getClientId());mFocusHolders.remove(focusEntry.getClientId());removeBlockerFromBlockedFocusLosersLocked(focusEntry);sendFocusLossLocked(focusEntry.getAudioFocusInfo(),AudioManager.AUDIOFOCUS_LOSS);logFocusEvent("Did not gained delayed audio focus for "+ focusEntry.getClientId());}}}

流程三:如果mDelayRequest为null的话,就要从mFocusLoser中找下一个焦点持有者了

代码三:private void removeBlockerAndRestoreUnblockedFocusLosersLocked(FocusEntry deadEntry) {// Remove this entry from the blocking list of any pending requestsIterator<FocusEntry> it = mFocusLosers.values().iterator();while (it.hasNext()) {FocusEntry entry = it.next();// Remove the retiring entry from all blocker listsentry.removeBlocker(deadEntry);// Any entry whose blocking list becomes empty should regain focusif (entry.isUnblocked()) {Log.i(TAG, "Restoring unblocked entry " + entry.getClientId());// Pull this entry out of the focus losers listit.remove();// Add it back into the focus holders listmFocusHolders.put(entry.getClientId(), entry);dispatchFocusGainedLocked(entry.getAudioFocusInfo());}}}

流程四:如果从mFocusHolders和mFocusLosers中没有找到要释放的这个FocusEntry,并且这个要释放的FocusEntry和mDelayedRequest有着相同的clientId,就把mDelayerRequest赋值为null

    private void removeDelayedAudioFocusRequestLocked(AudioFocusInfo afi) {if (mDelayedRequest != null && afi.getClientId().equals(mDelayedRequest.getClientId())) {mDelayedRequest = null;}}

更多推荐

Android CarAudioFocus详解(二)

本文发布于:2024-02-06 08:15:46,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1747686.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:详解   Android   CarAudioFocus

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!