android媒体焦点音量压低/暂停逻辑源码简析
客户端通过AudioManager
的调用requestAudioFocus
请求焦点的过程中,其他媒体客户端会因媒体焦点丢失进行媒体压低或暂停,下面从framework层简单分析其过程。
首先调用AudioManager
的requestAudioFocus
方法:
//AudioManager.java@SystemApi@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {...registerAudioFocusRequest(afr);final IAudioService service = getService();final int status;...synchronized (mFocusRequestsLock) {try {// TODO status contains result and generation counter for ext policystatus = service.requestAudioFocus(afr.getAudioAttributes(),afr.getFocusGain(), mICallBack,mAudioFocusDispatcher,clientId,getContext().getOpPackageName() /* package name */, afr.getFlags(),ap != null ? ap.cb() : null,sdk);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}...}return focusReceiver.requestResult();}
AudioManager
与系统服务AudioService
通信,调用AudioService
的requestAudioFocus
:
//AudioService.javapublic int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,IAudioPolicyCallback pcb, int sdk) {...return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,clientId, callingPackageName, flags, sdk,forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));}
接着调用MediaFocusControl
的方法:
//MediaFocusControl.javaprotected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,int sdk, boolean forceDuck) {...// propagate the focus change through the stackif (!mFocusStack.empty()) {propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);}...}
MediaFocusControl
通过propagateFocusLossFromGain_syncAf
方法同步处理焦点丢失:
@GuardedBy("mAudioFocusLock")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 gainfor (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*/);}}
接着调用FocusRequester
的handleFocusLossFromGain
方法再到handleFocusLoss
方法
//FocusRequester.java@GuardedBy("MediaFocusControl.mAudioFocusLock")void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck){try {if (focusLoss != mFocusLossReceived) {...handled = mFocusController.duckPlayers(frWinner, this, forceDuck);...}
其中mFocusController
即MediaFocusControl
MediaFocusControl
的requestAudioFocus
方法中创建FocusRequester
传入
final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
其中this
即MediaFocusControl
对象。
回到MediaFocusControl
的duckPlayers
方法:
//MediaFocusControl.java@Overridepublic boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);}
其中mFocusEnforcer
即PlaybackActivityMonitor
,在创建MediaFocusControl
对象时传入。
private final PlaybackActivityMonitor mPlaybackMonitor;mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
来到PlaybackActivityMonitor
执行duckPlayers
方法,通过apcsToDuck.add(apc)
添加到压低媒体apc(AudioPlaybackConfiguration)列表。
//PlaybackActivityMonitor.java@Overridepublic boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {...apcsToDuck.add(apc);...mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);}return true;}
其中mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck)
为内部类DuckingManager
根据mDuckers
获取内部类DuckedApp
,遍历apcsToDuck
去addDuck
音量压低逻辑。
//PlaybackActivityMonitor.javaprivate static final class DuckingManager {private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {if (DEBUG) { Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); }if (!mDuckers.containsKey(uid)) {mDuckers.put(uid, new DuckedApp(uid));}final DuckedApp da = mDuckers.get(uid);for (AudioPlaybackConfiguration apc : apcsToDuck) {da.addDuck(apc, false /*skipRamp*/);}}
内部类DuckedApp
,遍历apcsToDuck
去addDuck
音量压低逻辑:
//PlaybackActivityMonitor.javaprivate static final class DuckedApp {private final int mUid;private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();DuckedApp(int uid) {mUid = uid;}...void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {...try {apc.getPlayerProxy().applyVolumeShaper(FLY_DUCK_VSHAPE,VolumeShaper.Operation.PLAY);mDuckedPlayers.add(piid);} catch (Exception e) {Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);}}
DuckedApp
中执行apc.getPlayerProxy().applyVolumeShaper
进行音量压低,apc即AudioPlaybackConfiguration
。
存在AudioPlaybackConfiguration
的Map,在trackPlayer
时添加:
//PlaybackActivityMonitor.java private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =new HashMap<Integer, AudioPlaybackConfiguration>();public int trackPlayer(PlayerBase.PlayerIdCard pic) {final AudioPlaybackConfiguration apc =new AudioPlaybackConfiguration(pic, newPiid,Binder.getCallingUid(), Binder.getCallingPid());apc.init();synchronized(mPlayerLock) {mPlayers.put(newPiid, apc);}return newPiid;}
trackPlayer
方法在PlayerBase
的baseRegisterPlayer
方法中调用。
//PlayerBase.java/*** Call from derived class when instantiation / initialization is successful*/protected void baseRegisterPlayer() {...try {newPiid = getService().trackPlayer(new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));} catch (RemoteException e) {Log.e(TAG, "Error talking to audio service, player will not be tracked", e);}mPlayerIId = newPiid;}
AudioPlaybackConfiguration
中getPlayerProxy
获取IPlayerShell
的getIPlayer()
得到的是IPlayer实例,内部类IPlayerShell
获取的IPlayer
即为PlayerBase.PlayerIdCard pic
的参数mIPlayer
。
回到AudioService
中通过PlayerBase
的方法baseRegisterPlayer
调用getService().trackPlayer( new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)))
IPlayerWrapper
即为IPlayer
的实现类执行applyVolumeShaper
调用PlayerBase
实现类的playerApplyVolumeShaper
进行压低
MediaPlayer
、SoundPool
、AudioTrack
实现了PlayerBase
, 构造函数中调用baseRegisterPlayer
进行媒体注册,方法playerApplyVolumeShaper
调用native_applyVolumeShaper
进行压低。
VolumeShaper
实现音频音量控制,调用mediaPlayer
的playerApplyVolumeShaper
方法。
以上为媒体焦点音量压低的简单逻辑分析。
android媒体焦点音量压低/暂停逻辑源码简析相关推荐
- 【Android项目】本地FM收音机开发及源码简析
[Android项目]本地FM收音机开发及源码简析 目录 1.概述 2.收音机的基本原理 3.收音机其他信息 RDS功能 4.Android开发FM收音机源码解析 5.App层如何设计本地FM应用 6 ...
- django源码简析——后台程序入口
django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...
- java ArrayList 概述 与源码简析
ArrayList 概述 与源码简析 1 ArrayList 创建 ArrayList<String> list = new ArrayList<>(); //构造一个初始容量 ...
- Spring Boot源码简析 @EnableTransactionManagement
相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...
- ffmpeg实战教程(十三)iJKPlayer源码简析
要使用封装优化ijk就必须先了解ffmpeg,然后看ijk对ffmpeg的C层封装! 这是我看ijk源码时候的笔记,比较散乱.不喜勿喷~ ijk源码简析: 1.ijkplayer_jni.c 封装的播 ...
- 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...
- (Ajax)axios源码简析(三)——请求与取消请求
传送门: axios源码简析(一)--axios入口文件 axios源码简析(二)--Axios类与拦截器 axios源码简析(三)--请求与取消请求 请求过程 在Axios.prototype.re ...
- Log-Pilot 源码简析
Log-Pilot 源码简析 简单介绍 源码简析 Pilot结构体 Piloter接口 main函数 Pilot.Run Pilot.New Pilot.watch Pilot.processEven ...
- Android污点分析工具flowdroid源码简析
flowdroid是一款对Android app进行风险分析的应用,下面深入其源码对其工作的流程进行相关的探究. 1.准备 a)下载相关源码(包括soot.heros.jasmin.soot-info ...
最新文章
- JavaScript中substr()和substring的区别
- appium---【Mac】Appium-Doctor提示WARN:“ ios_webkit_debug_proxy cannot be found”解决方案...
- ML之回归预测:利用多个算法模型(LassoR、KernelRidgeR、ElasticNetR、GBR、LGBMR、XGBR)对国内某平台上海2020年6月份房价数据集【12+1】进行回归预测
- Linux学习之第二课时--linux命令格式及命令概述
- C++学习之路 | PTA乙级—— 1050 螺旋矩阵 (25 分)(精简)
- 蓝牙扫描过程解析_智慧定位系统之蓝牙网关在室内定位技术的原理浅析-新导智能...
- magento开启模板路径提示
- 干货!3 个重要因素,带你看透 AI 技术架构方案的可行性
- 项目 11 部署与运行
- 去除Xcode6创建工程时自带的storyboard
- 计算机技术基础的心得体会,计算机技术基础学习心得体会5篇范文.docx
- 抖音做综艺,差点意思
- 打印机出现另存为xps_win10系统打印文件弹出另存为xps/pdf的处理方法
- Unity笔记-29-ARPG游戏项目-13-打击感(卡肉)与僵直感
- 关于自编码器的核心点理解
- 软件测试入门自学笔记(1)测试用例设计
- 乐华娱乐IPO搁浅:王一博是旗下艺人 CMC阿里字节是股东
- 数据结构与算法之插入排序(含改进版)
- 为什么delphi编译生成的exe文件这么大?
- 带你攻破这家30K的offer(解读招聘的要求)