Статьи

Управление несколькими источниками звука в Android с помощью Audio Focus

Звук — отличный способ привлечь внимание пользователя, дать отзыв интерфейса или погрузить игрока в игру. Представьте, что все приложения пытались воспроизводить звуки одновременно. Это приведет к неразборчивой какофонии и заставит пользователей тянуться к кнопке отключения звука. Android предоставляет простой API для воспроизведения музыки и звуковых эффектов и управления различными источниками. API аудио-фокусировки Android позволяет приложению запрашивать «аудио-фокус» и уведомляет приложение о потере фокуса, чтобы оно могло реагировать. В этом уроке я покажу, как использовать эти API в ваших собственных приложениях.

Вы можете найти окончательный код этого руководства на GitHub .

Запрос аудио-фокуса для вашего приложения

Вы должны запросить фокусировку звука в своем приложении перед воспроизведением любого звука. Для этого вам нужно получить экземпляр AudioManager . Когда у вас есть экземпляр, вы можете использовать requestAudioFocus .

Создайте новое приложение с пустым действием и замените содержимое activity_main.xml следующим:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:text="Audio Focus" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnRequestFocus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Request Audio Focus"/> <Button android:id="@+id/btnReleaseFocus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Release Audio Focus"/> </LinearLayout> </RelativeLayout> 

Этот макет использует стиль LinearLayout который содержит две кнопки. Один, чтобы запросить фокус и один, чтобы выпустить его. Обновите содержимое MainActivity.java следующим образом:

 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnRequestFocus = (Button)findViewById(R.id.btnRequestFocus); btnRequestFocus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean gotFocus = requestAudioFocusForMyApp(MainActivity.this); if(gotFocus) { //play audio. } } }); Button btnReleaseFocus = (Button)findViewById(R.id.btnReleaseFocus); btnReleaseFocus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Stop playing audio. releaseAudioFocusForMyApp(MainActivity.this); } }); } private boolean requestAudioFocusForMyApp(final Context context) { AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); // Request audio focus for playback int result = am.requestAudioFocus(null, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { Log.d("AudioFocus", "Audio focus received"); return true; } else { Log.d("AudioFocus", "Audio focus NOT received"); return false; } } void releaseAudioFocusForMyApp(final Context context) { AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); am.abandonAudioFocus(null); } } 

Это действие добавляет слушателей щелчка для обеих кнопок. Когда пользователь нажимает кнопку « Запросить фокусировку звука» , он получает экземпляр AudioManager путем вызова API getSystemService и передает контекст Context.AUDIO_SERVICE . Получив AudioManager , вы можете запросить фокусировку, вызвав API requestAudioFocus который принимает AudioManager.OnAudioFocusChangeListener качестве первого параметра. Вы увидите это более подробно позже, но пока передайте null . Второй параметр — это тип потока, который может быть одним из следующих, в зависимости от типа звука, который вы хотите использовать:

  • STREAM_ALARM : звук является сигналом тревоги.
  • STREAM_MUSIC : аудио — это медиа, музыка, видео, фоновый звук и т. Д.
  • STREAM_NOTIFICATION : аудио для уведомления.
  • STREAM_RING : звук звонка телефона.
  • STREAM_SYSTEM : звук является системным звуком.
  • STREAM_DTMF : звук является тоном DTFM.

Третий параметр — это тип фокуса, который вы хотите, например, постоянный или переходный фокус. Он принимает один из следующих параметров:

  • AUDIOFOCUS_GAIN : когда вы хотите, чтобы фокусировался в течение долгого времени, чтобы воспроизводить аудио в течение длительного времени.
  • AUDIOFOCUS_GAIN_TRANSIENT : когда вы хотите сосредоточиться на короткое время.
  • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK : когда вы хотите сфокусироваться на короткое время, и другие приложения могут «пригнуться» вместо остановки звука.

Как только вы вызовите requestAudioFocus с соответствующими параметрами, API вернет AUDIOFOCUS_REQUEST_GRANTED или AUDIOFOCUS_REQUEST_FAILED . Запрос на фокусировку звука может быть неудачным, если звук с более высоким приоритетом, такой как телефонный звонок, выполняется, и вам следует начинать воспроизведение аудио только в случае requestAudioFocus API requestAudioFocus .

Вышеуказанное действие запрашивало фокусировку звука с типом потока STREAM_MUSIC и продолжительностью AUDIOFOCUS_GAIN для воспроизведения медиафайла в течение длительного времени. После завершения аудио отпустите фокус с abandonAudioFocus API abandonAudioFocus . В примере приложения это происходит, когда пользователь нажимает кнопку Release Audio Focus .

Пример приложения

Обработка событий аудиофокуса в приложении

Когда ваше приложение получает фокус, оно может передать AudioManager.OnAudioFocusChangeListener который предоставляет обратные вызовы, когда происходит изменение фокуса.

Предположим, что ваше приложение получает аудио-фокус, а другое приложение запрашивает временный аудио-фокус, фокус будет отдан другому приложению. Android уведомит ваше приложение через OnAudioFocusChangeListener чтобы ваше приложение могло реагировать на изменения. Как только другое приложение откажется от своего фокуса, ваше приложение вновь обретет фокус и получит соответствующий обратный вызов. Концептуально это похоже на события жизненного цикла onStart , OnStop как onStart , OnStop , OnPause , OnResume и т. Д.

Чтобы получать события фокуса, вам нужно передать экземпляр AudioManager.OnAudioFocusChangeListener requestAudioFocus и abandonAudioFocus . Обновите код MainActivity.java до следующего, чтобы получить обратные вызовы:

 public class MainActivity extends Activity { private final static String TAG = "AudioFocus"; private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: Log.i(TAG, "AUDIOFOCUS_GAIN"); //restart/resume your sound break; case AudioManager.AUDIOFOCUS_LOSS: Log.e(TAG, "AUDIOFOCUS_LOSS"); //Loss of audio focus for a long time //Stop playing the sound break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: Log.e(TAG, "AUDIOFOCUS_LOSS_TRANSIENT"); //Loss of audio focus for a short time //Pause playing the sound break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: Log.e(TAG, "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); //Loss of audio focus for a short time. //But one can duck. Lower the volume of playing the sound break; default: // } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnRequestFocus = (Button)findViewById(R.id.btnRequestFocus); btnRequestFocus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean gotFocus = requestAudioFocusForMyApp(MainActivity.this); if(gotFocus) { //play audio. } } }); Button btnReleaseFocus = (Button)findViewById(R.id.btnReleaseFocus); btnReleaseFocus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Stop playing audio. releaseAudioFocusForMyApp(MainActivity.this); } }); } private boolean requestAudioFocusForMyApp(final Context context) { AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); // Request audio focus for playback int result = am.requestAudioFocus(mOnAudioFocusChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { Log.d(TAG, "Audio focus received"); return true; } else { Log.d(TAG, "Audio focus NOT received"); return false; } } void releaseAudioFocusForMyApp(final Context context) { AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); am.abandonAudioFocus(mOnAudioFocusChangeListener); } } 

В AudioManager.OnAudioFocusChangeListener вам необходимо переопределить функцию onAudioFocusChange которой передается событие изменения фокуса.

События могут быть одним из следующих:

  • AUDIOFOCUS_LOSS : Аудио фокус теряется приложением. Вы должны прекратить воспроизведение звука и освободить все активы, приобретенные для воспроизведения звука.
  • AUDIOFOCUS_LOSS_TRANSIENT : Аудио фокус теряется приложением в течение короткого периода времени. Вы должны приостановить звук.
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK : Аудио фокус теряется приложением на короткий период времени. Вы можете продолжить воспроизведение звука, но следует уменьшить громкость.
  • AUDIOFOCUS_GAIN : Ваше приложение восстановило фокус аудио после потери. Вам следует перезапустить его и увеличить громкость, если она уменьшалась в предыдущем событии.

утенок

Иногда системе Android или другим приложениям может потребоваться воспроизводить короткие высокоприоритетные звуки. В этих случаях он будет запрашивать фокусировку звука как AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK . Если ваше приложение удерживало звуковой фокус, вы получите событие как AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK и вам следует продолжать воспроизводить звук, но уменьшите громкость. Если вы используете стандартный Android MediaPlayer вы можете уменьшить громкость с setVolume функции setVolume .

Уважение Аудио Фокус

Представьте себе конференцию, на которой все выступающие говорили в микрофон одновременно. Это был бы полный хаос, и зрители уйдут.

Хотя ваше приложение не обязано уважать фокус системы Android или других приложений, это хороший опыт для пользователя. Если вы этого не сделаете, ваше приложение будет получать плохие отзывы или частые удаления.

То, что приложение делает для реагирования на эти события, зависит от предполагаемой функциональности, но важно учитывать это.

Если у вас есть какие-либо комментарии или вопросы, пожалуйста, дайте мне знать ниже .