客户端通过AudioManager的调用requestAudioFocus请求焦点的过程中,其他媒体客户端会因媒体焦点丢失进行媒体压低或暂停,下面从framework层简单分析其过程。
首先调用AudioManagerrequestAudioFocus方法:

    //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通信,调用AudioServicerequestAudioFocus

    //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*/);}}

接着调用FocusRequesterhandleFocusLossFromGain方法再到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);...}

其中mFocusControllerMediaFocusControl
MediaFocusControlrequestAudioFocus方法中创建FocusRequester传入

            final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);

其中thisMediaFocusControl对象。

回到MediaFocusControlduckPlayers方法:

    //MediaFocusControl.java@Overridepublic boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);}

其中mFocusEnforcerPlaybackActivityMonitor,在创建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,遍历apcsToDuckaddDuck音量压低逻辑。

    //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,遍历apcsToDuckaddDuck音量压低逻辑:

    //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方法在PlayerBasebaseRegisterPlayer方法中调用。

   //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;}

AudioPlaybackConfigurationgetPlayerProxy获取IPlayerShellgetIPlayer()得到的是IPlayer实例,内部类IPlayerShell获取的IPlayer即为PlayerBase.PlayerIdCard pic的参数mIPlayer

回到AudioService中通过PlayerBase的方法baseRegisterPlayer调用getService().trackPlayer( new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)))
IPlayerWrapper即为IPlayer的实现类执行applyVolumeShaper调用PlayerBase实现类的playerApplyVolumeShaper进行压低
MediaPlayerSoundPoolAudioTrack实现了PlayerBase, 构造函数中调用baseRegisterPlayer进行媒体注册,方法playerApplyVolumeShaper调用native_applyVolumeShaper进行压低。

VolumeShaper实现音频音量控制,调用mediaPlayerplayerApplyVolumeShaper方法。

以上为媒体焦点音量压低的简单逻辑分析。

android媒体焦点音量压低/暂停逻辑源码简析相关推荐

  1. 【Android项目】本地FM收音机开发及源码简析

    [Android项目]本地FM收音机开发及源码简析 目录 1.概述 2.收音机的基本原理 3.收音机其他信息 RDS功能 4.Android开发FM收音机源码解析 5.App层如何设计本地FM应用 6 ...

  2. django源码简析——后台程序入口

    django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...

  3. java ArrayList 概述 与源码简析

    ArrayList 概述 与源码简析 1 ArrayList 创建 ArrayList<String> list = new ArrayList<>(); //构造一个初始容量 ...

  4. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  5. ffmpeg实战教程(十三)iJKPlayer源码简析

    要使用封装优化ijk就必须先了解ffmpeg,然后看ijk对ffmpeg的C层封装! 这是我看ijk源码时候的笔记,比较散乱.不喜勿喷~ ijk源码简析: 1.ijkplayer_jni.c 封装的播 ...

  6. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  7. (Ajax)axios源码简析(三)——请求与取消请求

    传送门: axios源码简析(一)--axios入口文件 axios源码简析(二)--Axios类与拦截器 axios源码简析(三)--请求与取消请求 请求过程 在Axios.prototype.re ...

  8. Log-Pilot 源码简析

    Log-Pilot 源码简析 简单介绍 源码简析 Pilot结构体 Piloter接口 main函数 Pilot.Run Pilot.New Pilot.watch Pilot.processEven ...

  9. Android污点分析工具flowdroid源码简析

    flowdroid是一款对Android app进行风险分析的应用,下面深入其源码对其工作的流程进行相关的探究. 1.准备 a)下载相关源码(包括soot.heros.jasmin.soot-info ...

最新文章

  1. JavaScript中substr()和substring的区别
  2. appium---【Mac】Appium-Doctor提示WARN:“ ios_webkit_debug_proxy cannot be found”解决方案...
  3. ML之回归预测:利用多个算法模型(LassoR、KernelRidgeR、ElasticNetR、GBR、LGBMR、XGBR)对国内某平台上海2020年6月份房价数据集【12+1】进行回归预测
  4. Linux学习之第二课时--linux命令格式及命令概述
  5. C++学习之路 | PTA乙级—— 1050 螺旋矩阵 (25 分)(精简)
  6. 蓝牙扫描过程解析_智慧定位系统之蓝牙网关在室内定位技术的原理浅析-新导智能...
  7. magento开启模板路径提示
  8. 干货!3 个重要因素,带你看透 AI 技术架构方案的可行性
  9. 项目 11 部署与运行
  10. 去除Xcode6创建工程时自带的storyboard
  11. 计算机技术基础的心得体会,计算机技术基础学习心得体会5篇范文.docx
  12. 抖音做综艺,差点意思
  13. 打印机出现另存为xps_win10系统打印文件弹出另存为xps/pdf的处理方法
  14. Unity笔记-29-ARPG游戏项目-13-打击感(卡肉)与僵直感
  15. 关于自编码器的核心点理解
  16. 软件测试入门自学笔记(1)测试用例设计
  17. 乐华娱乐IPO搁浅:王一博是旗下艺人 CMC阿里字节是股东
  18. 数据结构与算法之插入排序(含改进版)
  19. 为什么delphi编译生成的exe文件这么大?
  20. 带你攻破这家30K的offer(解读招聘的要求)

热门文章

  1. 虚拟机(VMware)如何设置共享文件夹
  2. 深圳高中女生街头版someone like you
  3. WebService 理论详解、JWS(Java Web Service) 快速入门
  4. 品牌对比 | 佰草集 VS 膜法世家
  5. 谈谈奋斗里陆涛为什么不爱米莱
  6. 笔记——输入框循环使用
  7. java 大臣的旅费_蓝桥杯2013-java大臣的旅费
  8. HTML5基础之代码入门
  9. 2022京东618具体活动时间和优惠节点
  10. 历史上十大著名思想实验