Mengelola fokus audio

Dua atau beberapa aplikasi Android dapat memutar audio ke aliran output yang sama secara bersamaan, dan sistem akan memadukan semuanya. Meskipun secara teknis mengesankan, hal ini dapat memberikan pengalaman pengguna yang buruk. Agar beberapa aplikasi musik tidak diputar secara bersamaan, Android memperkenalkan gagasan fokus audio. Hanya satu aplikasi yang dapat menerima fokus audio pada satu waktu.

Saat perlu meng-output audio, aplikasi Anda harus meminta fokus audio. Jika memiliki fokus, aplikasi dapat memutar suara. Namun, setelah memperoleh fokus audio, Anda mungkin tidak dapat mempertahankannya sampai pemutaran audio selesai. Aplikasi lain dapat meminta fokus, yang akan mengakhiri fokus audio Anda saat ini. Jika hal itu terjadi, aplikasi Anda harus menjeda pemutaran atau menurunkan volumenya agar pengguna dapat mendengar sumber audio baru dengan lebih mudah.

Sebelum Android 12 (API level 31), fokus audio tidak dikelola oleh sistem. Jadi, meskipun developer aplikasi dianjurkan untuk mematuhi panduan fokus audio, jika aplikasi terus diputar dengan keras bahkan setelah kehilangan fokus audio di perangkat yang menjalankan Android 11 (API level 30) atau yang lebih rendah, sistem tidak dapat mencegahnya. Namun, perilaku aplikasi ini menyebabkan pengalaman pengguna yang buruk dan sering kali dapat menyebabkan pengguna meng-uninstal aplikasi yang berperilaku buruk.

Aplikasi audio yang dirancang dengan baik harus mengelola fokus audio sesuai dengan pedoman umum berikut:

  • Panggil requestAudioFocus() tepat sebelum mulai memutar dan pastikan panggilan menampilkan AUDIOFOCUS_REQUEST_GRANTED. Lakukan panggilan ke requestAudioFocus() dalam callback onPlay() sesi media Anda.

  • Saat aplikasi lain memperoleh fokus audio, hentikan atau jeda pemutaran, atau perkecil (yaitu, kurangi) volume.

  • Saat pemutaran berhenti (misalnya, saat aplikasi tidak memiliki apa pun untuk diputar), tinggalkan fokus audio. Aplikasi Anda tidak harus mengabaikan fokus audio jika pengguna menjeda pemutaran, tetapi mungkin melanjutkan pemutaran nanti.

  • Gunakan AudioAttributes untuk mendeskripsikan jenis audio yang sedang diputar aplikasi Anda. Misalnya, untuk aplikasi yang memutar ucapan, tentukan CONTENT_TYPE_SPEECH.

Fokus audio ditangani secara berbeda bergantung pada versi Android yang berjalan:

Android 12 (API level 31) atau yang lebih baru
Fokus audio dikelola oleh sistem. Sistem memaksa pemutaran audio dari aplikasi untuk memudar saat aplikasi lain meminta fokus audio. Sistem juga membisukan pemutaran audio saat panggilan masuk diterima.
Android 8.0 (API level 26) hingga Android 11 (API level 30)
Fokus audio tidak dikelola oleh sistem, tetapi menyertakan beberapa perubahan yang diperkenalkan mulai dari Android 8.0 (API level 26).
Android 7.1 (API level 25) dan yang lebih rendah
Fokus audio tidak dikelola oleh sistem, dan aplikasi mengelola fokus audio menggunakan requestAudioFocus() dan abandonAudioFocus().

Fokus audio di Android 12 dan yang lebih baru

Aplikasi media atau game yang menggunakan fokus audio seharusnya tidak memutar audio setelah kehilangan fokus. Di Android 12 (API level 31) dan yang lebih baru, sistem menerapkan perilaku ini. Jika aplikasi meminta fokus audio saat aplikasi lain memiliki fokus dan diputar, sistem akan memaksa aplikasi yang diputar untuk memudar. Penambahan fitur fade-out memberikan transisi yang lebih lancar saat beralih dari satu aplikasi ke aplikasi lainnya.

Perilaku fade out ini terjadi jika kondisi berikut terpenuhi:

  1. Aplikasi pertama yang sedang diputar memenuhi semua kriteria berikut:

  2. Aplikasi kedua meminta fokus audio dengan AudioManager.AUDIOFOCUS_GAIN.

Jika kondisi ini terpenuhi, sistem audio akan memudarkan aplikasi pertama. Di akhir fade out, sistem akan memberi tahu hilangnya fokus ke aplikasi pertama. Pemutar aplikasi tetap dibisukan hingga aplikasi meminta fokus audio lagi.

Perilaku fokus audio yang ada

Anda juga harus mengetahui kasus lain yang melibatkan pengalihan fokus audio.

Pengecilan volume otomatis

Pengecilan volume otomatis (mengurangi sementara tingkat audio satu aplikasi sehingga aplikasi lain dapat terdengar jelas) diperkenalkan di Android 8.0 (API level 26).

Dengan membuat sistem menerapkan pengecilan volume, Anda tidak perlu menerapkan pengecilan volume di aplikasi.

Pengecilan volume otomatis juga terjadi saat notifikasi audio mengambil fokus dari aplikasi yang sedang diputar. Awal pemutaran notifikasi disinkronkan dengan pengoptimalan penurunan volume.

Pengecilan volume otomatis terjadi jika kondisi berikut terpenuhi:

  1. Aplikasi pertama yang sedang diputar memenuhi semua kriteria berikut:

  2. Aplikasi kedua meminta fokus audio dengan AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Jika kondisi ini terpenuhi, sistem audio akan memeriksa semua pemutar yang aktif dari aplikasi pertama, sementara aplikasi kedua mendapatkan fokus. Jika aplikasi kedua mengabaikan fokus, aplikasi akan kehilangan fokus. Aplikasi pertama tidak diberi tahu saat kehilangan fokus, sehingga tidak perlu melakukan apa pun.

Perhatikan bahwa pengecilan volume otomatis tidak dilakukan saat pengguna mendengarkan konten ucapan karena pengguna mungkin melewatkan beberapa program. Misalnya, panduan suara untuk rute mobil tidak dibisukan.

Membisukan pemutaran audio saat ini untuk panggilan telepon masuk

Beberapa aplikasi tidak berperilaku baik dan terus memutar audio selama panggilan telepon. Situasi ini memaksa pengguna untuk menemukan dan membisukan audio atau keluar dari aplikasi yang mengganggu tersebut agar dapat mendengar panggilan mereka. Untuk mencegah hal ini, sistem dapat membisukan audio dari aplikasi lain saat ada panggilan masuk. Sistem memanggil fitur ini saat panggilan telepon masuk diterima dan aplikasi memenuhi kondisi berikut:

  • Aplikasi memiliki atribut penggunaan AudioAttributes.USAGE_MEDIA atau AudioAttributes.USAGE_GAME.
  • Aplikasi berhasil meminta fokus audio (peningkatan fokus apa pun) dan memutar audio.

Jika aplikasi terus diputar selama panggilan, pemutarannya akan dibisukan hingga panggilan berakhir. Namun, jika aplikasi mulai diputar selama panggilan, pemutar tersebut tidak akan dibisukan dengan asumsi bahwa pengguna memulai pemutaran secara sengaja.

Fokus audio di Android 8.0 hingga Android 11

Mulai dari Android 8.0 (API level 26), saat memanggil requestAudioFocus(), Anda harus memberikan parameter AudioFocusRequest. AudioFocusRequest berisi informasi tentang konteks audio dan kemampuan aplikasi Anda. Sistem menggunakan informasi ini untuk mengelola perolehan dan kehilangan fokus audio secara otomatis. Untuk melepaskan fokus audio, panggil metode abandonAudioFocusRequest() yang juga menggunakan AudioFocusRequest sebagai argumennya. Gunakan instance AudioFocusRequest yang sama saat Anda meminta dan meninggalkan fokus.

Untuk membuat AudioFocusRequest, gunakan AudioFocusRequest.Builder. Karena permintaan fokus harus selalu menentukan jenis permintaan, jenis tersebut disertakan dalam konstruktor untuk builder. Gunakan metode builder untuk menetapkan kolom lainnya dalam permintaan.

Kolom FocusGain wajib diisi; semua kolom lainnya bersifat opsional.

MetodeCatatan
setFocusGain() Kolom ini wajib diisi dalam setiap permintaan. Kolom ini menerima nilai yang sama dengan durationHint yang digunakan dalam panggilan ke requestAudioFocus() sebelum Android 8.0: AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, atau AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
setAudioAttributes() AudioAttributes mendeskripsikan kasus penggunaan untuk aplikasi Anda. Sistem akan memeriksanya saat aplikasi memperoleh dan kehilangan fokus audio. Atribut menggantikan konsep jenis streaming. Di Android 8.0 (API level 26) dan yang lebih baru, jenis streaming untuk operasi apa pun selain kontrol volume tidak digunakan lagi. Gunakan atribut yang sama dalam permintaan fokus dengan yang Anda gunakan di pemutar audio (seperti yang ditunjukkan pada contoh di bawah tabel ini).

Gunakan AudioAttributes.Builder untuk menentukan atribut terlebih dahulu, lalu gunakan metode ini untuk menetapkan atribut ke permintaan.

Jika tidak ditentukan, AudioAttributes ditetapkan secara default ke AudioAttributes.USAGE_MEDIA.

setWillPauseWhenDucked() Saat aplikasi lain meminta fokus dengan AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, aplikasi yang memiliki fokus biasanya tidak menerima callback onAudioFocusChange() karena sistem dapat mengecilkan volume sendiri. Jika Anda perlu menjeda pemutaran, bukan mengecilkan volume, panggil setWillPauseWhenDucked(true), lalu buat dan tetapkan OnAudioFocusChangeListener, seperti dijelaskan dalam pengecilan volume otomatis.
setAcceptsDelayedFocusGain() Permintaan untuk fokus audio dapat gagal jika fokus dikunci oleh aplikasi lain. Metode ini memungkinkan perolehan fokus tertunda: kemampuan untuk memperoleh fokus secara asinkron saat fokus mulai tersedia.

Perhatikan bahwa perolehan fokus tertunda hanya berfungsi jika Anda juga menentukan AudioManager.OnAudioFocusChangeListener dalam permintaan audio, karena aplikasi Anda harus menerima callback ini agar dapat mengetahui bahwa fokus telah diberikan.

setOnAudioFocusChangeListener() OnAudioFocusChangeListener hanya diperlukan jika Anda juga menentukan willPauseWhenDucked(true) atau setAcceptsDelayedFocusGain(true) dalam permintaan.

Ada dua metode untuk menetapkan pemroses: satu dengan dan satu tanpa argumen pengendali. Pengendali ini adalah thread tempat pemroses berjalan. Jika Anda tidak menentukan pengendali, pengendali yang terkait dengan Looper utama akan digunakan.

Contoh berikut menunjukkan cara menggunakan AudioFocusRequest.Builder untuk membuat AudioFocusRequest serta meminta dan meninggalkan fokus audio:

Kotlin

// initializing variables for audio focus and playback management
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
    setAudioAttributes(AudioAttributes.Builder().run {
        setUsage(AudioAttributes.USAGE_GAME)
        setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        build()
    })
    setAcceptsDelayedFocusGain(true)
    setOnAudioFocusChangeListener(afChangeListener, handler)
    build()
}
val focusLock = Any()

var playbackDelayed = false
var playbackNowAuthorized = false

// requesting audio focus and processing the response
val res = audioManager.requestAudioFocus(focusRequest)
synchronized(focusLock) {
    playbackNowAuthorized = when (res) {
        AudioManager.AUDIOFOCUS_REQUEST_FAILED -> false
        AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
            playbackNow()
            true
        }
        AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> {
            playbackDelayed = true
            false
        }
        else -> false
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
override fun onAudioFocusChange(focusChange: Int) {
    when (focusChange) {
        AudioManager.AUDIOFOCUS_GAIN ->
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false
                    resumeOnFocusGain = false
                }
                playbackNow()
            }
        AudioManager.AUDIOFOCUS_LOSS -> {
            synchronized(focusLock) {
                resumeOnFocusGain = false
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying()
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // ... pausing or ducking depends on your app
        }
    }
}

Java

// initializing variables for audio focus and playback management
audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
playbackAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(playbackAttributes)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener(afChangeListener, handler)
        .build();
final Object focusLock = new Object();

boolean playbackDelayed = false;
boolean playbackNowAuthorized = false;

// requesting audio focus and processing the response
int res = audioManager.requestAudioFocus(focusRequest);
synchronized(focusLock) {
    if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
        playbackNowAuthorized = false;
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        playbackNowAuthorized = true;
        playbackNow();
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
        playbackDelayed = true;
        playbackNowAuthorized = false;
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
@Override
public void onAudioFocusChange(int focusChange) {
    switch (focusChange) {
        case AudioManager.AUDIOFOCUS_GAIN:
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false;
                    resumeOnFocusGain = false;
                }
                playbackNow();
            }
            break;
        case AudioManager.AUDIOFOCUS_LOSS:
            synchronized(focusLock) {
                resumeOnFocusGain = false;
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying();
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // ... pausing or ducking depends on your app
            break;
        }
    }
}

Pengecilan volume otomatis

Di Android 8.0 (API level 26), saat aplikasi lain meminta fokus dengan AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, sistem dapat mengecilkan dan memulihkan volume tanpa memanggil callback onAudioFocusChange() aplikasi.

Meskipun pengecilan volume otomatis merupakan perilaku yang dapat diterima untuk aplikasi pemutaran musik dan video, hal ini tidak berguna saat memutar konten lisan, seperti dalam aplikasi buku audio. Dalam kasus ini, aplikasi harus menjeda audio.

Jika Anda ingin aplikasi menjeda pemutaran, bukan mengurangi volumenya, saat diminta mengecilkan volume, buatlah OnAudioFocusChangeListener dengan metode callback onAudioFocusChange() yang menerapkan perilaku jeda/lanjutkan yang diinginkan. Panggil setOnAudioFocusChangeListener() untuk mendaftarkan pemroses, dan panggil setWillPauseWhenDucked(true) untuk memberi tahu sistem agar menggunakan callback Anda, bukan menjalankan pengecilan volume otomatis.

Perolehan fokus tertunda

Terkadang, sistem tidak dapat memenuhi permintaan fokus audio karena fokus "dikunci" oleh aplikasi lain, misalnya selama panggilan telepon. Dalam hal ini, requestAudioFocus() menampilkan AUDIOFOCUS_REQUEST_FAILED. Jika hal ini terjadi, aplikasi Anda tidak boleh melanjutkan pemutaran audio karena tidak memperoleh fokus.

Metode, setAcceptsDelayedFocusGain(true), yang memungkinkan aplikasi Anda menangani permintaan fokus secara asinkron. Dengan tanda ini ditetapkan, permintaan yang dibuat saat fokus terkunci akan menampilkan AUDIOFOCUS_REQUEST_DELAYED. Jika kondisi yang mengunci fokus audio tidak ada lagi, misalnya setelah panggilan telepon berakhir, sistem akan memenuhi permintaan fokus tertunda dan memanggil onAudioFocusChange() untuk memberi tahu aplikasi Anda.

Untuk menangani pemerolehan fokus tertunda, Anda harus membuat OnAudioFocusChangeListener dengan metode callback onAudioFocusChange() yang menerapkan perilaku yang diinginkan dan mendaftarkan pemroses dengan memanggil setOnAudioFocusChangeListener().

Fokus audio di Android 7.1 dan yang lebih lama

Saat memanggil requestAudioFocus(), Anda harus menentukan petunjuk durasi, yang dapat diikuti oleh aplikasi lain yang saat ini memegang fokus dan melakukan pemutaran:

  • Minta fokus audio permanen (AUDIOFOCUS_GAIN) jika Anda ingin memutar audio untuk waktu yang dapat diprediksi (misalnya, saat memutar musik) dan Anda ingin pemegang fokus audio sebelumnya menghentikan pemutaran.
  • Minta fokus sementara (AUDIOFOCUS_GAIN_TRANSIENT) jika Anda ingin memutar audio hanya untuk waktu singkat dan Anda ingin pemegang fokus audio sebelumnya menjeda pemutaran.
  • Minta fokus sementara dengan pengecilan audio (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) untuk menunjukkan bahwa Anda ingin memutar audio hanya dalam waktu singkat dan bahwa pemilik fokus sebelumnya dapat terus memutar jika "mengurangi" (menurunkan) output audionya. Kedua output audio dicampur ke dalam aliran audio. Pengecilan volume sangat cocok untuk aplikasi yang menggunakan aliran audio secara terputus-putus, seperti untuk audio petunjuk arah mengemudi.

Metode requestAudioFocus() juga memerlukan AudioManager.OnAudioFocusChangeListener. Pemroses ini harus dibuat dalam aktivitas atau layanan yang sama dengan yang memiliki sesi media Anda. Pemroses ini menerapkan callback onAudioFocusChange() yang diterima aplikasi Anda saat beberapa aplikasi lain memperoleh atau meninggalkan fokus audio.

Cuplikan berikut meminta fokus audio permanen pada aliran STREAM_MUSIC dan mendaftarkan OnAudioFocusChangeListener untuk menangani perubahan selanjutnya dalam fokus audio. (Pemroses perubahan dibahas dalam Merespons perubahan fokus audio.)

Kotlin

audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
lateinit var afChangeListener AudioManager.OnAudioFocusChangeListener

...
// Request audio focus for playback
val result: Int = audioManager.requestAudioFocus(
        afChangeListener,
        // Use the music stream.
        AudioManager.STREAM_MUSIC,
        // Request permanent focus.
        AudioManager.AUDIOFOCUS_GAIN
)

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

Java

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener afChangeListener;

...
// Request audio focus for playback
int result = audioManager.requestAudioFocus(afChangeListener,
                             // Use the music stream.
                             AudioManager.STREAM_MUSIC,
                             // Request permanent focus.
                             AudioManager.AUDIOFOCUS_GAIN);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

Setelah pemutaran selesai, panggil abandonAudioFocus().

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

// Abandon audio focus when playback complete
audioManager.abandonAudioFocus(afChangeListener);

Tindakan ini akan memberi tahu sistem bahwa Anda tidak lagi memerlukan fokus dan membatalkan pendaftaran OnAudioFocusChangeListener yang terkait. Jika Anda meminta fokus sementara, aplikasi yang menjeda atau mengecilkan volumenya akan diberi tahu sehingga dapat melanjutkan pemutaran atau memulihkan volumenya.

Merespons perubahan fokus audio

Saat aplikasi memperoleh fokus audio, aplikasi tersebut harus dapat melepaskannya saat aplikasi lain meminta fokus audio untuknya sendiri. Jika hal ini terjadi, aplikasi Anda akan menerima panggilan ke metode onAudioFocusChange() dalam AudioFocusChangeListener yang Anda tentukan saat aplikasi memanggil requestAudioFocus().

Parameter focusChange yang diteruskan ke onAudioFocusChange() menunjukkan jenis perubahan yang sedang terjadi. Parameter ini sesuai dengan petunjuk durasi yang digunakan oleh aplikasi yang mendapatkan fokus. Aplikasi Anda harus merespons dengan tepat.

Kehilangan fokus sementara
Jika perubahan fokus bersifat sementara (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK atau AUDIOFOCUS_LOSS_TRANSIENT), aplikasi Anda harus mengecilkan volumenya (jika Anda tidak mengandalkan pengecilan volume otomatis) atau menjeda pemutaran. Dalam kondisi sebaliknya, aplikasi Anda harus mempertahankan status yang sama.

Selama kehilangan fokus audio sementara, Anda harus terus memantau perubahan fokus audio dan bersiap melanjutkan pemutaran normal setelah memperoleh kembali fokus. Saat aplikasi pemblokir melepaskan fokus, Anda akan menerima callback (AUDIOFOCUS_GAIN). Pada tahap ini, Anda dapat memulihkan volume ke level normal atau memulai ulang pemutaran.

Kehilangan fokus permanen
Jika kehilangan fokus audio bersifat permanen (AUDIOFOCUS_LOSS), aplikasi lain akan memutar audio. Aplikasi Anda harus segera menjeda pemutaran, karena tidak akan pernah menerima callback AUDIOFOCUS_GAIN. Untuk memulai ulang pemutaran, pengguna harus melakukan tindakan eksplisit, seperti menekan kontrol transport play di UI notifikasi atau aplikasi.

Cuplikan kode berikut menunjukkan cara mengimplementasikan OnAudioFocusChangeListener dan callback onAudioFocusChange()-nya. Perhatikan penggunaan Handler untuk menunda callback stop saat kehilangan fokus audio bersifat permanen.

Kotlin

private val handler = Handler()
private val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
    when (focusChange) {
        AudioManager.AUDIOFOCUS_LOSS -> {
            // Permanent loss of audio focus
            // Pause playback immediately
            mediaController.transportControls.pause()
            // Wait 30 seconds before stopping playback
            handler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30))
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            // Pause playback
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // Lower the volume, keep playing
        }
        AudioManager.AUDIOFOCUS_GAIN -> {
            // Your app has been granted audio focus again
            // Raise volume to normal, restart playback if necessary
        }
    }
}

Java

private Handler handler = new Handler();
AudioManager.OnAudioFocusChangeListener afChangeListener =
  new AudioManager.OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
        // Permanent loss of audio focus
        // Pause playback immediately
        mediaController.getTransportControls().pause();
        // Wait 30 seconds before stopping playback
        handler.postDelayed(delayedStopRunnable,
          TimeUnit.SECONDS.toMillis(30));
      }
      else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
        // Pause playback
      } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // Lower the volume, keep playing
      } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
        // Your app has been granted audio focus again
        // Raise volume to normal, restart playback if necessary
      }
    }
  };

Pengendali menggunakan Runnable yang terlihat seperti ini:

Kotlin

private var delayedStopRunnable = Runnable {
    mediaController.transportControls.stop()
}

Java

private Runnable delayedStopRunnable = new Runnable() {
    @Override
    public void run() {
        getMediaController().getTransportControls().stop();
    }
};

Untuk memastikan stop tertunda tidak menjadi aktif jika pengguna memulai ulang pemutaran, panggil mHandler.removeCallbacks(mDelayedStopRunnable) sebagai respons terhadap perubahan status apa pun. Misalnya, panggil removeCallbacks() di onPlay(), onSkipToNext(), dll. untuk Callback Anda. Sebaiknya Anda juga memanggil metode ini di callback onDestroy() layanan Anda saat membersihkan resource yang digunakan oleh layanan Anda.