一、申请音频焦点的简单用法
private AudioManager mAudioManager;
private AudioAttributes mNavAudioAttrib;
mAudioManager = (AudioManager) mContext.getSystemService( Context.AUDIO_SERVICE );
mNavAudioAttrib = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build();
try {
AudioFocusRequest naviFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(mNavFocusListener)
.setAudioAttributes(mNavAudioAttrib)
.setFocusGain(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK).build();
//申请音频焦点,得到返回结果
int result = mAudioManager.requestAudioFocus(naviFocusRequest);
} catch (Exception e) {
Log.e(TAG, "Failed to set active focus", e);
}
private final AudioManager.OnAudioFocusChangeListener mNavFocusListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
Log.i(TAG, "Nav focus change:" + focusChange);
if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
setFocusText(AUDIO_FOCUS_STATE_GAIN);
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
setFocusText("loss");
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
setFocusText("loss,transient");
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
setFocusText("loss,transient,duck");
}
}
};
二、系统源码解析
相关源码:
\frameworks\base\media\java\android\media\AudioManager.java
\frameworks\base\services\core\java\com\android\server\audio\AudioService.java
\frameworks\base\services\core\java\com\android\server\audio\MediaFocusControl.java
\frameworks\base\services\core\java\com\android\server\audio\FocusRequester.java
关键接口:
mAudioManager.requestAudioFocus(naviFocusRequest);
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
if (afr == null) {
throw new NullPointerException("Illegal null AudioFocusRequest");
}
// this can only be checked now, not during the creation of the AudioFocusRequest instance
if (afr.locksFocus() && ap == null) {
throw new IllegalArgumentException(
"Illegal null audio policy when locking audio focus");
}
registerAudioFocusRequest(afr);
final IAudioService service = getService();
final int status;
int sdk;
try {
sdk = getContext().getApplicationInfo().targetSdkVersion;
} catch (NullPointerException e) {
// some tests don't have a Context
sdk = Build.VERSION.SDK_INT;
}
final String clientId = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
final BlockingFocusResultReceiver focusReceiver;
//1.申请音频焦点是多线程异步操作,需要同步锁
synchronized (mFocusRequestsLock) {
try {
//2.开始申请音频焦点,status为申请结果;分为内部策略和外部策略
// TODO status contains result and generation counter for ext policy
status = 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();
}
//3.音频焦点支持外部策略,这里两种结果:一是内部策略,直接返回申请音频焦点的结果status;二是外部策略,返回AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY,申请焦点的结果则由外部策略发送mAudioManager.setFocusRequestResult()来通知。
if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
// default path with no external focus policy
//如果是内部策略,则直接返回音频焦点结果
return status;
}
//4.外部策略所用的Map,用于保存多个线程申请音频焦点的情况
if (mFocusRequestsAwaitingResult == null) {
mFocusRequestsAwaitingResult =
new HashMap<String, BlockingFocusResultReceiver>(