android framework audiofocus机制
![此博文包含图片](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
数据结构
AudioService中维护了一个栈:Stack mFocusStack
栈里面的记录FocusStackEntry定义如下:
红色的两个变量,可以在log里面打印出来,可以看是哪个应用在申请和放弃audiofocus
获取和释放audiofocus,入栈、压栈以及出栈
这个栈保存了audiofoucs的使用情况,正常情况下,任何播放音频的应用都需要在播放前申请得到audiofocus,播放完之后申请放弃audiofocus;从代码角度来看就是:
播放前调用requestAudioFocus,如果成功则构造一个FocusStackEntry,并且放入mFocusStack栈顶;
如果此时栈不为空,则前面的栈顶元素就要压栈,对应的应用将失去焦点,
播放完调用abandonAudioFocus,从mFocusStack栈中删除栈顶记录;
这个时候如果栈不为空,则此时栈顶的应用将获取到audiofocus。
应用得到和失去焦点可以通过实现回调函数onAudioFocusChanged来监听,并且做相应逻辑的处理,比如说Music应用,如果获取到audiofocus就播放,如果audiofocus被别的应用如视频抢占了,就停止播放音乐,当视频播放完成之后,Music应用又将获取到焦点,然后又开始播放。
具体requestAudioFocus和abandonAudioFocus代码就不看了,
申请过程会做三个事情:
1:检查当前栈顶的元素是否是Phone应用占用,如果Phone处于占用状态,则获取失败;
2:获取成功,压栈之前,需要检查当前栈中是否已经有这个应用的记录,如果有的话就删除掉;
3:构造一条记录,并且入栈。
释放过程会有两个分支:
1:如果要释放的应用是在栈顶,则释放之后,还需要通知先在栈顶应用,其获得了audiofocus;
2:如果要释放的应用不是在栈顶,则只是移除这个记录,不需要更改当前audiofocus的占有情况。
实例解析:
播放音乐,然后又开始播放视频,播放视频的过程中来电,然后挂断电话,然后视频播放结束,最后音频播放结束
用下面的草图来说明全部的过程,简单明了,省去过多的文字
1:播放音乐,栈的情况
2:开始播放视频,栈的情况
3:此时来电,栈的情况
4:来电后,视频会释放焦点,但是Music一直不会释放,保存在栈中,此时栈的情况
5:挂断电话,Ring会释放audiofocus,此时audiofocus自动给栈顶的应用,栈的情况如下:
6,退出Phone界面,会进入视频播放界面,视频会重新request Audiofocus,入栈,Music被压栈
7:视频播放结束,Video释放audiofocus并且出栈,栈顶元素自动获得audiofocus
8:音乐播放结束,Music释放焦点,出栈,栈变为空
从上面的栈的变化图可以看出,Phone和Gallery应用每次播放和停止播放都会入栈和出栈操作,而Music被打断并不会出栈,只是失去栈顶的位置,并且失去audiofocus焦点
时序导致的问题
由于时序上的问题,Phone界面已经消失,跳到了视频播放界面,视频调用requestAudiofocus去获取焦点,但是此时Phone应用还没有来的及释放完焦点,导致视频request失败,但是视频也不管成功与失败,都会继续往下播放,等到Phone应用完全释放audiofocus完毕,最后栈中只存在了Music应用,自然而然Music获取到了audiofocus,也开始播放,这样就存在视频和Music同时在发声的问题。
而如果视频requestAudiofocus是发声在Phone应用完全abandonAudiofocus之后(此时焦点给了Music),这个时候视频能够从Music那里抢来audiofocus,开始播放,Music失去焦点从而又暂停不播放了,就没问题了。
这个问题可以在应用中修改去规避,每个应用都尽可能遵循audiofocus机制去实现,避免类似问题的产生