Ses odağını yönet

İki veya daha fazla Android uygulaması, aynı çıkış akışına aynı anda ses çalabilir ve sistem her şeyi bir araya getirir. Bu teknik açıdan etkileyici olsa da kullanıcı için çok can sıkıcı olabilir. Android, her müzik uygulamasının aynı anda çalmasını önlemek için ses odağı kavramını kullanır. Aynı anda yalnızca bir uygulama ses odağına sahip olabilir.

Uygulamanızın ses çıkışı yapması gerektiğinde ses odağı istemelidir. Odaklandığında ses çalabilir. Ancak ses odağını aldıktan sonra, çalma işlemini tamamlayana kadar bu odağı koruyamayabilirsiniz. Başka bir uygulama, ses odağını sizden alarak odaklanma isteğinde bulunabilir. Bu durumda, kullanıcıların yeni ses kaynağını daha kolay duyabilmesi için uygulamanızın oynatmayı duraklatması veya ses seviyesini düşürmesi gerekir.

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

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

  • Oynatmaya başlamadan hemen önce requestAudioFocus() yöntemini çağırın ve çağrının AUDIOFOCUS_REQUEST_GRANTED değerini döndürdüğünü doğrulayın. Medya oturumunuzun onPlay() geri çağırma işlevinde requestAudioFocus() çağrısını yapın.

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

  • Oynatma durduğunda (örneğin, uygulamada oynatılacak bir şey kalmadığında) ses odağını bırakın. Kullanıcı oynatmayı duraklatır ancak daha sonra devam ettirebilirse 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 sesi çalan uygulamalar için CONTENT_TYPE_SPEECH değerini belirtin.

Ses odağı, çalışan 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. Başka bir uygulama ses odağı istediğinde sistem, bir uygulamanın ses çalma işlemini zorla azaltır. Sistem, gelen arama olduğunda ses çalmayı da kapatır.
Android 8.0 (API düzeyi 26) - Android 11 (API düzeyi 30)
Ses odağı sistem tarafından yönetilmez ancak Android 8.0'dan (API düzeyi 26) 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. 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 uygulamayı yavaşça kapatmaya zorlar. Karartma efekti, bir uygulamadan diğerine geçişi daha yumuşak hale getirir.

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

  1. İlk olarak, o anda oynatılan uygulama aşağıdaki ölçütlerin tümünü karşılamalıdır:

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

Bu koşullar karşılandığında ses sistemi, ilk uygulamanın sesini kısar. Ses kısma işleminin sonunda sistem, ilk uygulamaya odak kaybını bildirir. Uygulama tekrar ses odağı isteyene kadar uygulamanın oynatıcılarının sesi kapalı kalır.

Mevcut ses odağı davranışları

Ses odağının değiştiği diğer durumları da bilmeniz gerekir.

Otomatik ses kısma

Otomatik ses kısma (bir uygulamanın ses seviyesini geçici olarak azaltarak diğerinin net bir şekilde duyulmasını sağlama) özelliği Android 8.0 (API düzeyi 26) ile kullanıma sunulmuştur.

Sistemin ses kısma özelliğini uygulamasını sağlayarak uygulamanızda ses kısma özelliğini uygulamanız gerekmez.

Otomatik ses kısma, sesli bildirim odağı oynatılan bir uygulamadan aldığında da gerçekleşir. Bildirim oynatmanın başlangıcı, ses kısma rampasının sonuyla senkronize edilir.

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

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

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

Bu koşullar karşılandığında ses sistemi, ikinci uygulama odaktayken ilk uygulamanın tüm etkin oynatıcılarının sesini kısar. İkinci uygulama odağı bıraktığında sesleri eski seviyesine getirir. İlk uygulama, odağı kaybettiğinde bildirim almaz. Bu nedenle herhangi bir işlem yapması gerekmez.

Kullanıcı konuşma içeriği dinlerken otomatik ses kısma işlemi yapılmaz. Bunun nedeni, kullanıcının programın bir kısmını kaçırabilmesidir. Örneğin, arabayla yol tarifi için sesli yardımın sesi kısılmaz.

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

Bazı uygulamalar düzgün çalışmaz ve telefon aramaları sırasında ses çalmaya devam eder. Bu durumda kullanıcı, aramasını duyabilmek için rahatsız edici uygulamayı bulup sesini kapatmak veya uygulamadan çıkmak zorunda kalır. Bunu önlemek için sistem, gelen arama olduğunda diğer uygulamalardan gelen sesleri kapatabilir. Sistem, gelen bir telefon araması alındığında ve bir uygulama aşağıdaki koşulları karşıladığında bu özelliği çağırır:

  • Uygulama, AudioAttributes.USAGE_MEDIA veya AudioAttributes.USAGE_GAME kullanım özelliğine sahiptir.
  • Uygulama, ses odağını (herhangi bir odak kazanımı) başarıyla istedi ve ses çalıyor.

Bir uygulama arama sırasında oynatmaya devam ederse oynatma, arama sona erene kadar sessize alınır. Ancak arama sırasında bir uygulama oynatılmaya başlarsa kullanıcının oynatmayı kasıtlı olarak başlattığı varsayılarak bu oynatıcının sesi kapatılmaz.

Android 8.0 ile Android 11 arasında ses odağı

Android 8.0 (API düzeyi 26) ile başlayarak requestAudioFocus() yöntemini çağırdığınızda bir AudioFocusRequest parametresi sağlamanız gerekir. AudioFocusRequest, uygulamanızın ses bağlamı ve özellikleri hakkında bilgi içerir. Sistem, ses odağının kazanılmasını ve kaybedilmesini otomatik olarak yönetmek için bu bilgileri kullanır. Ses odağını serbest bırakmak için bağımsız değişken olarak AudioFocusRequest alan abandonAudioFocusRequest() yöntemini çağırın. Odak isteğinde bulunurken ve odağı bırakırken aynı AudioFocusRequest örneğini kullanın.

AudioFocusRequest oluşturmak için AudioFocusRequest.Builder kullanın. Odaklanma isteğinde her zaman isteğin türü belirtilmesi gerektiğinden, tür, oluşturucunun yapıcı işlevine 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 bunları inceler. Özellikler, akış türü kavramının yerini alır. Android 8.0 (API düzeyi 26) ve sonraki sürümlerde, ses düzeyi kontrolleri dışındaki tüm işlemler için akış türleri kullanımdan kaldırılmıştır. Odak isteğinde, ses çalarınızda kullandığınız özellikleri kullanın (bu tablodan sonraki ö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 odak istediğinde, odaklanılan uygulama genellikle onAudioFocusChange() geri çağırma almaz çünkü sistem kısma işlemini kendi başına yapabilir. Sesi kısmak yerine oynatmayı duraklatmanız gerektiğinde setWillPauseWhenDucked(true) yöntemini çağırın ve otomatik ses kısma bölümünde açıklandığı gibi bir OnAudioFocusChangeListener oluşturup ayarlayın.
setAcceptsDelayedFocusGain() Ses odağı isteği, odak başka bir uygulama tarafından kilitlendiğinde başarısız olabilir. Bu yöntem, gecikmeli odak kazanmayı etkinleştirir: odağı kullanılabilir olduğunda eşzamansız olarak alma özelliği.

Gecikmeli odaklanmanın yalnızca ses isteğinde bir AudioManager.OnAudioFocusChangeListener de belirtirseniz çalıştığını unutmayın. Uygulamanızın, odağın verildiğini bilmesi için geri çağırmayı alması gerekir.

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

İşleyiciyi ayarlamanın iki yöntemi vardır: İşleyici bağımsız değişkeni olan ve olmayan. İş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 oluşturmak, ses odağı istemek ve ses odağını bırakmak için AudioFocusRequest.Builder öğesinin nasıl kullanılacağı 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ğırma işlevini ç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 kitap uygulaması gibi sözlü içerik oynatırken kullanışlı değildir. Bu durumda uygulama, sesi kısmak yerine duraklatmalıdır.

Uygulamanızın, sesini azaltmak yerine ses kısma 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() yöntemini, sisteme otomatik ses kısma yerine geri çağırma işlevinizi kullanmasını söylemek için ise setWillPauseWhenDucked(true) yöntemini çağırın.

Gecikmeli odaklanma

Sistem bazen ses odağı isteğini yerine getiremez. Bunun nedeni, odağın başka bir uygulama tarafından "kilitlenmiş" olmasıdır (ör. telefon görüşmesi sırasında). Bu durumda, requestAudioFocus(), AUDIOFOCUS_REQUEST_FAILED değerini döndürür. Bu durumda, uygulamanız odaklanmadığı için ses çalmaya devam etmemelidir.

Uygulamanızın odaklanma isteğini eşzamansız olarak işlemesini sağlayan setAcceptsDelayedFocusGain(true) yöntemi. 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 ortadan kalktığında (ör. telefon araması sona erdiğinde) sistem, bekleyen odak isteğini kabul eder ve uygulamanızı bilgilendirmek için onAudioFocusChange() yöntemini çağırır.

Gecikmeli odaklanma kazanımını işlemek için istenen davranışı uygulayan bir onAudioFocusChange() geri çağırma yöntemiyle OnAudioFocusChangeListener oluşturmanız ve setOnAudioFocusChangeListener() yöntemini çağırarak dinleyiciyi kaydetmeniz gerekir.

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

requestAudioFocus() yöntemini çağırdığınızda, o anda odaklanmış ve oynatmakta olan 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ğı (AUDIOFOCUS_GAIN) isteyin.
  • Yalnızca kısa bir süre ses çalmayı ve önceki sahibin çalmayı duraklatmasını beklediğinizde geçici odak (AUDIOFOCUS_GAIN_TRANSIENT) isteyin.
  • Yalnızca kısa bir süre için ses çalmayı beklediğinizi ve önceki odak sahibinin ses çıkışını "kısması" (azaltması) durumunda çalmaya devam etmesinin sorun olmadığını belirtmek için kısma (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) ile geçici odak isteyin. Her iki ses çıkışı da ses akışına karıştırılır. Ses azaltma, özellikle ses akışını aralıklı olarak kullanan uygulamalar (ör. arabayla yol tarifi) için uygundur.

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

Aşağıdaki snippet, akışta kalıcı ses odağı ister STREAM_MUSIC ve ses odağındaki sonraki değişiklikleri işlemek için bir OnAudioFocusChangeListener kaydeder. (Değişiklik dinleyici, 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() yöntemini çağırın.

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

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

Bu, sisteme artık odağa ihtiyacınız olmadığını bildirir ve ilişkili OnAudioFocusChangeListener kaydını iptal eder. Geçici odak istediyseniz bu, duraklatılan veya sesi kısılan bir uygulamaya oynatmaya devam edebileceğini ya da ses seviyesini geri yükleyebileceğini bildirir.

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

Bir uygulama ses odağını aldığında, başka bir uygulama ses odağı istediğinde bu odağı serbest bırakabilmelidir. Bu durumda uygulamanız, requestAudioFocus() yöntemini çağırdığında belirttiğiniz AudioFocusChangeListener içindeki onAudioFocusChange() yöntemine bir çağrı alır.

onAudioFocusChange() işlevine iletilen focusChange parametresi, gerçekleşen değişikliğin türünü belirtir. Bu, odağı alan uygulamanın kullandığı 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 sesi kısmalı (otomatik ses kısma özelliğini kullanmıyorsanız) veya oynatmayı duraklatmalı ancak bunun dışında aynı durumu korumalıdır.

Ses odağının geçici olarak kaybedilmesi sırasında ses odağındaki değişiklikleri izlemeye devam etmeniz ve odağı yeniden kazandığınızda normal oynatmayı sürdürmeye hazır olmanız gerekir. Engelleyen uygulama odağı bıraktığında bir geri çağırma (AUDIOFOCUS_GAIN) alırsınız. Bu noktada, ses seviyesini normal seviyeye geri yükleyebilir veya oynatmayı yeniden başlatabilirsiniz.

Kalıcı odak kaybı
Ses odağı kaybı kalıcıysa (AUDIOFOCUS_LOSS) başka bir uygulama ses oynatıyordur. Uygulamanız hiçbir zaman AUDIOFOCUS_GAIN geri çağırma işlevi almayacağı için oynatmayı hemen duraklatmalıdır. Oynatmayı yeniden başlatmak için kullanıcının bildirim veya uygulama kullanıcı arayüzündeki oynatma kontrolüne basmak gibi açık bir işlem yapması gerekir.

Aşağıdaki kod snippet'i, OnAudioFocusChangeListener ve onAudioFocusChange() geri çağırma işlevinin nasıl uygulanacağını gösterir. Ses odağının kalıcı olarak kaybedilmesi durumunda durdurma geri çağırma işlevini geciktirmek için Handler kullanıldığına dikkat 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, şuna benzer 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 girmemesi için herhangi bir durum değişikliğine yanıt olarak mHandler.removeCallbacks(mDelayedStopRunnable) yöntemini çağırın. Örneğin, geri çağırma işlevinizin onPlay(), onSkipToNext() vb. yöntemlerinde removeCallbacks() yöntemini ç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.