Ses odağını yönet

İki veya daha fazla Android uygulaması aynı çıkış akışında aynı anda ses çalabilir ve sistem tüm sesleri karıştırır. Bu durum teknik olarak etkileyici olsa da kullanıcıları çok sinirlendirebilir. Android, tüm müzik uygulamalarının aynı anda çalmasını önlemek için ses odaklanması kavramını sunar. Aynı anda yalnızca bir uygulama ses odağını tutabilir.

Uygulamanızın ses çıkışı yapması gerektiğinde ses odağı isteğinde bulunması gerekir. Odaklandığında ses çalabilir. Ancak ses odağını aldıktan sonra, oynatma işlemini bitirene kadar odağı koruyamayabilirsiniz. Başka bir uygulama odak isteğinde bulunabilir. Bu durumda, ses odağı üzerindeki kontrolünüz iptal edilir. Bu durumda, uygulamanızın yeni ses kaynağını daha kolay duyabilmeleri için kullanıcıların oynatmayı duraklatması veya sesini kısması gerekir.

Android 12'den (API düzeyi 31) önce ses odağı sistem tarafından yönetilmez. Bu nedenle, uygulama geliştiricilerin ses odağı yönergelerine uyması önerilse de Android 11 (API düzeyi 30) veya önceki sürümlerin yüklü olduğu bir cihazda ses odağını kaybettikten sonra bile yüksek sesle çalmaya devam eden uygulamalar sistem tarafından engellenemez. Ancak bu uygulama davranışı, kötü bir kullanıcı deneyimine yol açar ve genellikle kullanıcıların hatalı uygulamayı kaldırmasına neden olabilir.

İyi tasarlanmış bir ses uygulaması, ses odağını şu genel yönergelere göre yönetmelidir:

  • Oynamaya başlamadan hemen önce requestAudioFocus() numaralı telefonu arayın ve aramanın AUDIOFOCUS_REQUEST_GRANTED döndürdüğünü doğrulayın. Medya oturumunuzun onPlay() geri çağırmasında requestAudioFocus() işlevini çağırın.

  • Başka bir uygulama ses odağı kazandığında oynatmayı durdurun veya duraklatın ya da sesi kısın.

  • Oynatma durduğunda (örneğin, uygulamada oynatılacak başka içerik kalmadığında) ses odağını bırakın. Kullanıcı oynatmayı duraklatırsa ancak daha sonra oynatmaya devam edebilirse uygulamanızın ses odağını bırakması gerekmez.

  • Uygulamanızın çaldığı ses türünü açıklamak için AudioAttributes kullanın. Örneğin, konuşma oynatan uygulamalar için CONTENT_TYPE_SPEECH değerini belirtin.

Ses odağı, çalıştırılan Android sürümüne bağlı olarak farklı şekilde işlenir:

Android 12 (API düzeyi 31) veya sonraki sürümler
Ses odağı sistem tarafından yönetilir. Sistem, başka bir uygulama ses odağı istediğinde bir uygulamadan ses çalmayı zorla kapatır. Sistem, gelen arama olduğunda ses çalmayı da kapatır.
Android 8.0 (API düzeyi 26) ile Android 11 (API düzeyi 30)
Ses odağı sistem tarafından yönetilmez ancak Android 8.0 (API düzeyi 26) sürümünden itibaren kullanıma sunulan bazı değişiklikleri içerir.
Android 7.1 (API düzeyi 25) ve önceki sürümler
Ses odağı sistem tarafından yönetilmez ve uygulamalar ses odağını requestAudioFocus() ve abandonAudioFocus() kullanarak yönetir.

Android 12 ve sonraki sürümlerde ses odağı

Ses odağını kullanan bir medya veya oyun uygulaması, odağı kaybettikten sonra ses çalmamalıdır. Android 12 (API düzeyi 31) ve sonraki sürümlerde sistem bu davranışı zorunlu kılar. Bir uygulama, başka bir uygulama odaklanmış ve oynatılırken ses odağı istediğinde sistem, oynatılan uygulamanın sesini yavaş yavaş azaltır. Solma efektinin eklenmesi, bir uygulamadan diğerine geçişi daha sorunsuz hale getirir.

Bu solma davranışı aşağıdaki koşullar karşılandığında gerçekleşir:

  1. Şu anda oynatılan ilk uygulama, bu ölçütlerin tümünü karşılıyor:

  2. İkinci bir uygulama, AudioManager.AUDIOFOCUS_GAIN ile ses odağı istiyor.

Bu koşullar karşılandığında ses sistemi ilk uygulamayı yavaşça kapatır. Yavaşça kapatma işleminin sonunda sistem, ilk uygulamaya odak kaybı olduğunu bildirir. Uygulama, ses odağını tekrar isteyene kadar uygulamanın oynatıcıları sessiz kalır.

Mevcut ses odağı davranışları

Ses odağının değiştiği diğer durumlar hakkında da bilgi sahibi olmanız gerekir.

Otomatik ses kısma

Otomatik sesi kısma (bir uygulamanın ses seviyesini geçici olarak azaltarak başka bir uygulamanın net bir şekilde duyulmasını sağlama) özelliği Android 8.0 (API düzeyi 26) sürümünde kullanıma sunuldu.

Sistemde ducking uygulanması sayesinde uygulamanızda ducking uygulamanız gerekmez.

Otomatik ses azaltma, bir sesli bildirim oynatılan uygulamanın odağını aldığında da gerçekleşir. Bildirim oynatmanın başlangıcı, ses azaltma eğrisinin sonuyla senkronize edilir.

Aşağıdaki koşullar karşılandığında otomatik ses kısma gerçekleşir:

  1. Şu anda oynatılan ilk uygulama aşağıdaki tüm ölçütleri karşılıyor:

  2. İkinci bir uygulama, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ile ses odağı istiyor.

Bu koşullar karşılandığında ses sistemi, ikinci uygulama odaklanmışken birinci uygulamanın tüm etkin oynatıcılarının sesini kısar. İkinci uygulama odağı bıraktığında diğer uygulamalar da ayrılır. İlk uygulama, odak kaybedildiğinde bildirim almaz. Bu nedenle herhangi bir işlem yapması gerekmez.

Kullanıcı programın bir kısmını kaçırabileceği için, konuşma içeriklerini dinlerken otomatik ses kısma işleminin yapılmadığını unutmayın. Örneğin, sürüş yol tarifleri için sesli yönlendirme özelliği kısılmaz.

Gelen telefon aramaları için mevcut ses çalmayı sessize alma

Bazı uygulamalar düzgün çalışmaz ve telefon görüşmeleri sırasında ses çalmaya devam eder. Bu durumda kullanıcı, görüşmesini duyabilmek için sorunlu uygulamayı bulup sessize almaya veya uygulamadan çıkmaya zorlanır. Bunu önlemek için sistem, gelen arama olduğunda diğer uygulamalardan gelen sesi kapatabilir. Sistem, gelen bir telefon araması alındığında ve bir uygulama şu koşulları karşıladığında bu özelliği etkinleştirir:

  • Uygulamada AudioAttributes.USAGE_MEDIA veya AudioAttributes.USAGE_GAME kullanım özelliği var.
  • Uygulama, ses odağı isteğini başarıyla aldı (herhangi bir odak kazanımı) ve ses çalıyor.

Görüşme sırasında oynatmaya devam eden uygulamaların oynatması, görüşme sona erene kadar sessize alınır. Ancak, arama sırasında bir uygulama oynatmaya başlarsa kullanıcının oynatmayı kasıtlı olarak başlattığı varsayılarak bu oynatıcı sessize alınmaz.

Android 8.0-11 sürümlerinde ses odağı

Android 8.0 (API düzeyi 26) sürümünden itibaren, requestAudioFocus() işlevini çağırdığınızda AudioFocusRequest parametresini sağlamanız gerekir. AudioFocusRequest Uygulamanızın ses bağlamı ve özellikleri hakkında bilgiler içerir. Sistem, ses odağının kazancını ve kaybını otomatik olarak yönetmek için bu bilgileri kullanır. Ses odağını serbest bırakmak için abandonAudioFocusRequest() yöntemini çağırın. Bu yöntem, bağımsız değişken olarak AudioFocusRequest alır. Hem odaklanma isteğinde bulunduğunuzda hem de odaklanmayı bıraktığınızda aynı AudioFocusRequest örneğini kullanın.

AudioFocusRequest oluşturmak için AudioFocusRequest.Builder kullanın. Odaklanma isteğinde her zaman istek türü belirtilmesi gerektiğinden tür, oluşturucunun oluşturucu için olanına dahil edilir. İsteğin diğer alanlarını ayarlamak için oluşturucunun yöntemlerini kullanın.

FocusGain alanı zorunludur, diğer tüm alanlar isteğe bağlıdır.

YöntemNotlar
setFocusGain() Bu alan her istekte zorunludur. Android 8.0 öncesi requestAudioFocus() çağrısında kullanılan durationHint ile aynı değerleri alır: AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK veya AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
setAudioAttributes() AudioAttributes, uygulamanızın kullanım alanını açıklar. Sistem, bir uygulama ses odağı kazandığında ve kaybettiğinde bu alanlara bakar. Özellikler, yayın türü kavramının yerini alır. Android 8.0 (API düzeyi 26) ve sonraki sürümlerde, ses kontrolleri dışındaki tüm işlemler için akış türleri kullanımdan kaldırılmıştır. Odaklanma isteğinde, ses oynatıcınızda kullandığınız özelliklerin aynısını kullanın (bu tablonun ardından gelen örnekte gösterildiği gibi).

Önce özellikleri belirtmek için AudioAttributes.Builder kullanın, ardından özellikleri isteğe atamak için bu yöntemi kullanın.

Belirtilmezse AudioAttributes varsayılan olarak AudioAttributes.USAGE_MEDIA olur.

setWillPauseWhenDucked() Başka bir uygulama AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ile odaklanma isteğinde bulunduğunda, odaklanmış uygulama genellikle onAudioFocusChange() geri çağırma işlemi almaz çünkü sistem kendi başına odaklanma isteğini karşılayabilir. Sesi azaltmak yerine oynatmayı duraklatmanız gerektiğinde setWillPauseWhenDucked(true) işlevini çağırın ve otomatik ses azaltma bölümünde açıklandığı gibi bir OnAudioFocusChangeListener oluşturup ayarlayın.
setAcceptsDelayedFocusGain() Odak başka bir uygulama tarafından kilitlendiğinde ses odağı isteği başarısız olabilir. Bu yöntem, gecikmeli odak kazanımını sağlar: odak kullanılabilir hale geldiğinde eşzamansız olarak odak kazanma özelliği.

Uygulamanızın odağın verildiğini bilmesi için geri aramayı alması gerektiğinden, gecikmeli odak kazanımının yalnızca ses isteğinde bir AudioManager.OnAudioFocusChangeListener belirtirseniz çalıştığını unutmayın.

setOnAudioFocusChangeListener() OnAudioFocusChangeListener yalnızca istekte willPauseWhenDucked(true) veya setAcceptsDelayedFocusGain(true) de belirtirseniz gereklidir.

Dinleyiciyi ayarlamak için iki yöntem vardır: biri işleyici bağımsız değişkeniyle, diğeri ise işleyici bağımsız değişkeni olmadan. İşleyici, dinleyicinin çalıştığı iş parçacığıdır. Bir işleyici belirtmezseniz ana Looper ile ilişkili işleyici kullanılır.

Aşağıdaki örnekte, AudioFocusRequest.Builder kullanarak AudioFocusRequest oluşturma, ses odağı isteme ve ses odağını bırakma işlemleri gösterilmektedir:

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

Otomatik ses kısma

Android 8.0'da (API düzeyi 26) başka bir uygulama AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ile odak istediğinde sistem, uygulamanın onAudioFocusChange() geri çağırmasını çağırmadan sesi kısabilir ve geri yükleyebilir.

Otomatik ses kısma, müzik ve video oynatma uygulamaları için kabul edilebilir bir davranış olsa da sesli içerik oynatılırken (ör. sesli kitap uygulamalarında) kullanışlı değildir. Bu durumda uygulama, sesi kısma yerine duraklatmalıdır.

Uygulamanızın sesini azaltmak yerine eğilme istendiğinde duraklatılmasını istiyorsanız istenen duraklatma/devam ettirme davranışını uygulayan bir onAudioFocusChange() geri çağırma yöntemiyle OnAudioFocusChangeListener oluşturun. Dinleyiciyi kaydetmek için setOnAudioFocusChangeListener()'ı, sisteme otomatik ses kısma işlemi yerine geri arama özelliğini kullanmasını söylemek için setWillPauseWhenDucked(true)'ı arayın.

Gecikmeli odak kazanma

Bazen odak başka bir uygulama tarafından "kilitlendiği" için (ör. telefon görüşmesi sırasında) sistem ses odağı isteğini veremez. Bu durumda, requestAudioFocus() işlevi AUDIOFOCUS_REQUEST_FAILED değerini döndürür. Bu durumda, odaklanmadığı için uygulamanız ses oynatmaya devam etmemelidir.

Uygulamanızın odaklanma isteğini eşzamansız olarak işlemesini sağlayan yöntem (setAcceptsDelayedFocusGain(true)). Bu işaret ayarlandığında, odak kilitliyken yapılan bir istek AUDIOFOCUS_REQUEST_DELAYED değerini döndürür. Ses odağını kilitleyen koşul artık mevcut olmadığında (ör. telefon görüşmesi sona erdiğinde) sistem, bekleyen odak isteğini verir ve uygulamanızı bilgilendirmek için onAudioFocusChange() işlevini çağırır.

Odak kazanma işleminin gecikmesini yönetmek için istenen davranışı uygulayan bir onAudioFocusChange() geri çağırma yöntemiyle OnAudioFocusChangeListener oluşturmanız ve setOnAudioFocusChangeListener() çağırarak dinleyiciyi kaydetmeniz gerekir.

Android 7.1 ve önceki sürümlerde ses odağı

Arama yaptığınızda requestAudioFocus() şu anda odaklanmış ve oynayan başka bir uygulama tarafından dikkate alınabilecek bir süre ipucu belirtmeniz gerekir:

  • Öngörülebilir bir süre boyunca ses çalmayı planladığınızda (ör. müzik çalarken) ve ses odağının önceki sahibinin çalmayı durdurmasını beklediğinizde kalıcı ses odağı isteğinde bulunun (AUDIOFOCUS_GAIN).
  • Yalnızca kısa bir süre ses çalmayı ve önceki tutucunun çalmayı duraklatmasını beklediğinizde geçici odak isteğinde bulunun (AUDIOFOCUS_GAIN_TRANSIENT).
  • Yalnızca kısa bir süre ses çalmayı beklediğinizi ve önceki odak sahibinin ses çıkışını "kısıp" (düşürüp) çalmaya devam etmesinin sorun olmayacağını belirtmek için ducking (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) ile geçici odak isteğinde bulunun. Her iki ses çıkışı da ses akışına karıştırılır. Ses azaltma, özellikle sesli yol tarifleri gibi ses akışını aralıklı olarak kullanan uygulamalar için uygundur.

requestAudioFocus() yöntemi için de AudioManager.OnAudioFocusChangeListener gerekir. Bu dinleyici, medya oturumunuzun sahibi olan etkinliğin veya hizmetin içinde oluşturulmalıdır. Başka bir uygulama ses odağını aldığında veya bıraktığında uygulamanızın aldığı geri çağırmayı onAudioFocusChange() uygular.

Aşağıdaki snippet, akışta STREAM_MUSIC kalıcı ses odağı ister ve ses odağındaki sonraki değişiklikleri işlemek için OnAudioFocusChangeListener kaydeder. (Değişiklik dinleyicisi, Ses odağı değişikliğine yanıt verme bölümünde ele alınmıştır.)

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
}

Oynatmayı bitirdiğinizde abandonAudioFocus() numaralı telefonu arayın.

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

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

Bu işlem, sisteme artık odaklanmaya ihtiyacınız olmadığını bildirir ve ilişkili OnAudioFocusChangeListener öğesinin kaydını siler. Geçici odaklanma isteğinde bulunduysanız bu, duraklatılmış veya sesi kısılmış bir uygulamaya oynatmaya devam edebileceğini ya da sesini geri yükleyebileceğini bildirir.

Ses odağı değişikliğine yanıt verme

Bir uygulama ses odağı kazandığında, başka bir uygulama kendisi için ses odağı istediğinde bu odağı bırakabilmelidir. Bu durumda, uygulamanız requestAudioFocus()'ı çağırdığında belirttiğiniz AudioFocusChangeListener içindeki onAudioFocusChange() yöntemine yapılan bir çağrı alır.

onAudioFocusChange()'e aktarılan focusChange parametresi, gerçekleşen değişiklik türünü gösterir. Odak kazanan uygulama tarafından kullanılan süre ipucuna karşılık gelir. Uygulamanız uygun şekilde yanıt vermelidir.

Geçici odak kaybı
Odak değişikliği geçiciyse (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK veya AUDIOFOCUS_LOSS_TRANSIENT), uygulamanız otomatik ses kısma özelliğini kullanmıyorsanız sesi kısmalı veya oynatmayı duraklatmalı ancak aksi takdirde aynı durumu korumalıdır.

Ses odağının geçici olarak kaybolduğu durumlarda, ses odağındaki değişiklikleri izlemeye devam etmeli ve odağı tekrar kazandığınızda normal oynatmaya devam etmeye hazır olmalısınız. Engelleme uygulaması odağı bıraktığında geri çağırma (AUDIOFOCUS_GAIN) alırsınız. Bu noktada, sesi normal seviyeye geri yükleyebilir veya oynatmayı yeniden başlatabilirsiniz.

Odaklanmanın kalıcı olarak kaybolması
Ses odağı kaybı kalıcıysa (AUDIOFOCUS_LOSS) başka bir uygulama ses çalıyor demektir. Uygulamanız, AUDIOFOCUS_GAIN geri çağırması almayacağı için oynatmayı hemen duraklatmalıdır. Oynatmayı yeniden başlatmak için kullanıcının, bildirimde veya uygulama kullanıcı arayüzünde oynatma taşıma kontrolüne basma gibi açık bir işlem yapması gerekir.

Aşağıdaki kod snippet'inde, OnAudioFocusChangeListener ve onAudioFocusChange() geri çağırmasının nasıl uygulanacağı gösterilmektedir. Ses odağı kalıcı olarak kaybolduğunda durdurma geri arama işlemini geciktirmek için Handler kullanıldığını fark edin.

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

İşleyici, aşağıdaki gibi bir Runnable kullanır:

Kotlin

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

Java

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

Kullanıcı oynatmayı yeniden başlatırsa gecikmeli durdurmanın devreye girmemesini sağlamak için herhangi bir durum değişikliğine yanıt olarak mHandler.removeCallbacks(mDelayedStopRunnable) işlevini çağırın. Örneğin, geri çağırma işlevinizin onPlay(), onSkipToNext() vb. içinde removeCallbacks() işlevini çağırın. Ayrıca, hizmetiniz tarafından kullanılan kaynakları temizlerken hizmetinizin onDestroy() geri çağırma işlevinde de bu yöntemi çağırmanız gerekir.