问题
在9.0版本上,A先申请音频焦点,B先申请再释放音频焦点后,A也不能收到onAudioFocusChange回调,导致不能继续播放。
但是在7.1版本,这种情况是会正常播放的。
分析过程
查看源码,主要的焦点控制逻辑在MediaFocusControl类,查看B申请焦点requestAudioFocus方法:
其中有调用propagateFocusLossFromGain_syncAf 方法,通知焦点栈中其他元素丢失焦点(也就是A)。
//9.0 8.0 MediaFocusControl.java
private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
boolean forceDuck) {
final List<String> clientsToRemove = new LinkedList<String>();
// going through the audio focus stack to signal new focus, traversing order doesn't
// matter as all entries respond to the same external focus gain
for (FocusRequester focusLoser : mFocusStack) {
final boolean isDefinitiveLoss =
focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
if (isDefinitiveLoss) {
clientsToRemove.add(focusLoser.getClientId());
}
}
for (String clientToRemove : clientsToRemove) {
removeFocusStackEntry(clientToRemove, false /*signal*/,
true /*notifyFocusFollowers*/);
}
}
看上去已经很像了,先判断栈内元素是否要remove, 然后遍历remove。
//9.0 8.0 FocusRequest.java
boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck) {
final int focusLoss = focusLossForGainRequest(focusGain);
handleFocusLoss(focusLoss, frWinner, forceDuck);
return (focusLoss == AudioManager.AUDIOFOCUS_LOSS);
}
private int focusLossForGainRequest(int gainRequest) {
switch(gainRequest) {
case AudioManager.AUDIOFOCUS_GAIN:
switch(mFocusLossReceived) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_NONE:
return AudioManager.AUDIOFOCUS_LOSS;
}
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
switch(mFocusLossReceived) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_NONE:
return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
case AudioManager.AUDIOFOCUS_LOSS:
return AudioManager.AUDIOFOCUS_LOSS;
}
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
switch(mFocusLossReceived) {
case AudioManager.AUDIOFOCUS_NONE:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
case AudioManager.AUDIOFOCUS_LOSS:
return AudioManager.AUDIOFOCUS_LOSS;
}
default:
Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
return AudioManager.AUDIOFOCUS_NONE;
}
}
focusLossForGainRequest 根据新申请的应用B的参数返回其他应用A的丢失焦点,判断如果拿到的是AUDIOFOCUS_LOSS,则将其从栈中移除。
然而对于7.0来说 没有移出栈这个过程:
private void propagateFocusLossFromGain_syncAf(int focusGain) {
// going through the audio focus stack to signal new focus, traversing order doesn't
// matter as all entries respond to the same external focus gain
Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
stackIterator.next().handleExternalFocusGain(focusGain);
}
}
总结
在8.0以上系统,如果一个应用申请音频焦点使用的是AudioManager.AUDIOFOCUS_GAIN,则其他应用会失去焦点,且被音频焦点栈中移除,后续也不再受到回调,也就不能自动获取焦点了。
因为我们的需求不是很强烈,就没有修改。可以考虑根据使用场景对第二个申请焦点的应用改用AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE或者AUDIOFOCUS_GAIN_TRANSIENT.如果是系统厂商也可以考虑对上面修改源码。
其他
详细的音频焦点流程可参考网上7.0的分析文档