Ses odağını yönet

İki veya daha fazla Android uygulaması, sesi aynı çıkış akışına eş zamanlı olarak çalabilir ve sistem her şeyi karıştırır. Bu teknik olarak etkileyici olsa da kullanıcılar için son derece üzücü olabilir. Android, tüm müzik uygulamalarının aynı anda çalmasını önlemek için ses odağı fikrini kullanıma sunar. Aynı anda yalnızca bir uygulama ses odağını tutabilir.

Uygulamanızın ses çıkışını vermesi gerektiğinde sese odaklanmayı istemesi gerekir. Odaklanma olduğunda ses çalabilir. Ancak ses odağını aldıktan sonra, çalana kadar bu sesi tutamayabilirsiniz. Başka bir uygulama odaklanma isteğinde bulunabilir. Bu da sesin odaklanmasını önlüyor. Böyle bir durumda, kullanıcıların yeni ses kaynağını daha kolay duyabilmesi için uygulamanız oynatmayı duraklatmalı veya ses düzeyini düşürmelidir.

Android 12'den (API düzeyi 31) önceki sürümlerde ses odağı sistem tarafından yönetilmez. Bu nedenle, uygulama geliştiricilerin ses odağı yönergelerine uymaları önerilir. Android 11 (API düzeyi 30) veya daha düşük sürümleri çalıştıran bir cihazda ses odağını kaybettikten sonra bile uygulama yüksek sesle çalmaya devam ederse sistem bunu engelleyemez. Ancak bu uygulama davranışı, kötü bir kullanıcı deneyimine yol açar ve çoğu zaman kullanıcıların hatalı çalışan uygulamayı kaldırmasına yol açabilir.

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

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

  • Başka bir uygulama sese odaklandığında, çalmayı durdurun veya duraklatın ya da sesi azaltın (yani sesi azaltın).

  • Oynatma durduğunda (örneğin, uygulamada oynatılacak hiçbir şey kalmadığında) ses odağının kesilmesi. Kullanıcı oynatmayı duraklatır ancak daha sonra devam ettirebilirse uygulamanızın ses odağından vazgeçmesi gerekmez.

  • Uygulamanızın çaldığı sesin türünü tanımlamak için AudioAttributes ifadesini kullanın. Örneğin, konuşma çalan uygulamalar için CONTENT_TYPE_SPEECH değerini belirtin.

Ses odağı, çalışan Android sürümüne bağlı olarak farklı şekilde ele alınır:

Android 12 (API düzeyi 31) veya sonraki sürümler
Ses odağı sistem tarafından yönetilir. Başka bir uygulama ses odağı isteğinde bulunduğunda sistem, bir uygulamadaki ses çalma işleminin yavaşça kaybolmasını sağlar. Ayrıca, gelen bir arama geldiğinde sistem, çalan sesin sesini kapatır.
Android 8.0 (API düzeyi 26) ile Android 11 (API düzeyi 30) arası
Ses odağı, sistem tarafından yönetilmese de 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. Uygulamalar ise ses odağını requestAudioFocus() ve abandonAudioFocus() kullanarak yönetir.

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

Ses odağı kullanan bir medya veya oyun uygulaması, odağı kaybolduktan sonra ses çalmamalıdır. Android 12 (API düzeyi 31) ve sonraki sürümlerde sistem bu davranışı uygular. Bir uygulama, odakta ve oyun oynatılırken başka bir uygulama ses odağı istediğinde sistem oynatılan uygulamayı karartmaya zorlar. Şeffaflaştırma efektinin eklenmesi, bir uygulamadan diğerine geçerken daha yumuşak bir geçiş sağlar.

Bu sönme davranışı, aşağıdaki koşullar karşılandığında ortaya çıkar:

  1. Şu anda oynatılan ilk uygulama aşağıdaki kriterlerin 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 uygulamadaki sesi yavaşça sona erer. Kaybolmanın sonunda sistem, ilk uygulamaya odak kaybı olduğunu bildirir. Uygulama tekrar ses odağı isteğinde bulunana kadar uygulamanın oynatıcılarının sesi kapalı kalır.

Mevcut ses odağı davranışları

Ses odağında değişiklik yapılmasını gerektiren diğer durumların da farkında olmalısınız.

Otomatik kısma

Android 8.0'da (API düzeyi 26) otomatik sesi kapatma (bir uygulamanın ses düzeyini, diğerinin net bir şekilde duyulması için geçici olarak düşürme) kullanıma sunuldu.

Sistemin içeriği kısmayı uygulamasını sağlayarak uygulamanızda bölmeyi uygulamak zorunda kalmazsınız.

Otomatik kısma, uygulamadaki bir sesli bildirimin odağını çektiğinde de gerçekleşir. Bildirim oynatmanın başlangıcı, ses kısma rampasının sonuyla senkronize edilir.

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

  1. Şu anda oynatılan ilk uygulama aşağıdaki kriterlerin tümünü karşılamaktadır:

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

Bu koşullar karşılandığında ses sistemi, ilk uygulamadaki tüm etkin oynatıcıları azaltırken ikinci uygulama odaklanır. İkinci uygulama odaklanmayı bıraktığında duyuşturma çözümlenir. İlk uygulama odağını kaybettiğinde bildirim gönderilmediğinden uygulamanın bir şey yapması gerekmez.

Kullanıcı programın bir kısmını atlayabileceğinden, otomatik kısmanın kullanıcı konuşma içeriğini dinlerken gerçekleştirilmediğini unutmayın. Örneğin, arabayla yol tarifleri için sesli yardım boğulmaz.

Gelen telefon aramaları için geçerli ses çalmayı kapat

Bazı uygulamalar düzgün çalışmaz ve telefon aramaları sırasında ses çalmaya devam eder. Bu durum, kullanıcıyı aramayı duymak için rahatsız edici uygulamayı bulup sesini kapatmaya veya kapatmaya zorlar. Sistem bunu önlemek için, gelen bir arama sırasında diğer uygulamaların sesini kapatabilir. Bir gelen telefon araması alındığında ve bir uygulama aşağıdaki koşulları karşıladığında, sistem bu özelliği çağırır:

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

Arama sırasında bir uygulama çalmaya devam ederse arama sona erene kadar oynatmanın sesi kapatılır. Bununla birlikte, arama sırasında bir uygulama oynamaya başlarsa kullanıcının videoyu bilerek oynatmaya başladığı varsayılarak oynatıcının sesi kapatılmaz.

Android 8.0 ile Android 11 arasındaki sürümlerde ses odağı

Android 8.0 (API düzeyi 26) sürümünden itibaren, requestAudioFocus() çağrısı yaptığınızda bir AudioFocusRequest parametresi sağlamanız gerekir. AudioFocusRequest, uygulamanızın ses bağlamı ve özellikleri hakkında bilgiler içerir. Sistem bu bilgileri, ses odağı kazanımını ve kaybını otomatik olarak yönetmek için kullanır. Ses odağını serbest bırakmak için AudioFocusRequest bağımsız değişkenini de alan abandonAudioFocusRequest() yöntemini çağırın. Odaklanma isteğinde bulunurken ve odadan ayrıldığınızda aynı AudioFocusRequest örneğini kullanın.

AudioFocusRequest oluşturmak için AudioFocusRequest.Builder kullanın. Odak isteğinin her zaman istek türünü belirtmesi gerektiğinden tür, oluşturucunun oluşturucusuna eklenir. İ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ğını kazanıp kaybettiğinde bunları dikkate alır. Özellikler, akış türü kavramının yerini alır. Android 8.0 (API düzeyi 26) ve sonraki sürümlerde ses seviyesi kontrolleri dışındaki işlemler için akış türleri kullanımdan kaldırılmıştır. Odak isteğinde, ses çalarınızda kullandığınız özelliklerin aynılarını kullanın (bu tabloyu aşağıdaki örnekte gösterildiği gibi).

Önce özellikleri belirtmek için bir 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 değerine ayarlanır.

setWillPauseWhenDucked() AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ile odaklanmayı sağlayan başka bir uygulama istediğinde, sistem bunu kendi başına yapabilir. Bu nedenle, odaklanan uygulama genellikle onAudioFocusChange() geri çağırma yapmaz. Sesi kısmak yerine çalmayı duraklatmanız gerektiğinde setWillPauseWhenDucked(true) öğesini arayın ve otomatik kısma bölümünde açıklandığı gibi bir OnAudioFocusChangeListener oluşturup ayarlayın.
setAcceptsDelayedFocusGain() Odak başka bir uygulama tarafından kilitlendiğinde, bir ses odaklama isteği başarısız olabilir. Bu yöntem, gecikmeli odaklanma kazancını, yani kullanılabilir olduğunda odağı eşzamansız olarak elde etme olanağı sağlar.

Uygulamanızın odağın verildiğini anlayabilmesi için geri çağırmayı alması gerektiğinden, gecikmeli odak kazancı yalnızca ses isteğinde bir AudioManager.OnAudioFocusChangeListener de belirtirseniz çalışır.

setOnAudioFocusChangeListener() OnAudioFocusChangeListener, yalnızca istekte willPauseWhenDucked(true) veya setAcceptsDelayedFocusGain(true) belirtmeniz durumunda gereklidir.

İşleyiciyi ayarlamanın iki yöntemi vardır: işleyici bağımsız değişkeni olan ve olmayan iki yöntem. İşleyici, işleyicinin üzerinde çalıştığı iş parçacığıdır. İşleyici belirtmezseniz ana Looper ile ilişkilendirilmiş işleyici kullanılır.

Aşağıdaki örnekte, AudioFocusRequest oluşturmak ve ses odağını istemek ve terk etmek 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 kısma

Android 8.0'da (API düzeyi 26), başka bir uygulama AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ile odaklanma isteğinde bulunduğunda sistem, uygulamanın onAudioFocusChange() geri çağırmasını çağırmadan sesi azaltıp geri yükleyebilir.

Otomatik kısma, müzik ve video oynatma uygulamalarında kabul edilebilir bir davranış olsa da, sesli kitap uygulamaları gibi sözlü içerik oynatıldığında faydalı değildir. Bu durumda uygulamanın duraklatılması gerekir.

Uygulamanızın sesini kısmak yerine kısması istendiğinde duraklatılmasını istiyorsanız istenen duraklatma/devam ettirme davranışını uygulayan onAudioFocusChange() geri çağırma yöntemine sahip bir OnAudioFocusChangeListener oluşturun. Dinleyiciyi kaydetmek için setOnAudioFocusChangeListener() numaralı telefonu arayın ve sisteme, otomatik kısma işlemi yapmak yerine geri aramanızı kullanmasını söylemek için setWillPauseWhenDucked(true) numaralı telefonu arayın.

Gecikmeli odak kazancı

Bazen odak başka bir uygulama tarafından "kilitlendiğinde" (ör. telefon araması sırasında) sistem, ses odaklanması için bir isteği kabul edemez. Bu durumda requestAudioFocus(), AUDIOFOCUS_REQUEST_FAILED değerini döndürür. Böyle bir durumda uygulamanız odaklanmadığı için sesli çalma işlemine 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 artık mevcut olmadığında (ör. bir telefon aramasının sona ermesi durumunda) sistem bekleyen odaklanma isteğini kabul eder ve onAudioFocusChange() çağırarak uygulamanızı bildirir.

Odaklanmanın gecikmeli kazanımını yönetmek için istenen davranışı uygulayan onAudioFocusChange() geri çağırma yöntemine sahip bir OnAudioFocusChangeListener oluşturmanız ve setOnAudioFocusChangeListener() çağrısı yaparak işleyiciyi kaydetmeniz gerekir.

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

requestAudioFocus() çağrısı yaptığınızda, o anda odağı tutan ve oynatılan başka bir uygulama tarafından geçerli olabilecek bir süre ipucu belirtmeniz gerekir:

  • Öngörülebilir gelecekte ses çalmayı planlıyorsanız (örneğin, müzik çalarken) ve ses odağının önceki sahibinin çalmayı durdurmasını bekliyorsanız kalıcı ses odağı (AUDIOFOCUS_GAIN) isteyin.
  • Sesin yalnızca kısa bir süreliğine çalınmasını ve önceki kullanıcının çalmayı duraklatmasını beklediğiniz durumlarda geçici odak (AUDIOFOCUS_GAIN_TRANSIENT) isteğinde bulunun.
  • Sesin yalnızca kısa bir süre için çalınmasını beklediğinizi ve ses çıkışını "kıstığı" (kısıtlıyorsa) önceki odak sahibinin içeriği oynatmaya devam edebileceğini belirtmek için bırakma (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) ile geçici odaklanma isteğinde bulunun. Her iki ses çıkışı da ses akışına karıştırılır. Sesi kısma özelliği, özellikle ses akışını kesintili olarak kullanan uygulamalar (ör. sesli arabayla yol tarifleri) için uygundur.

requestAudioFocus() yöntemi için AudioManager.OnAudioFocusChangeListener de gerekir. Bu işleyici, medya oturumunuzun sahibi olan etkinlik veya hizmette oluşturulmalıdır. Bu model, başka bir uygulama ses odağını elde ettiğinde veya terk ettiğinde uygulamanızın aldığı onAudioFocusChange() geri çağırmasını uygular.

Aşağıdaki snippet, STREAM_MUSIC akışına kalıcı bir ses odağı ister ve ses odağında sonraki değişiklikleri işlemek için bir 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ı tamamladığınızda 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 odaklanmanızın gerekli olmadığını bildirir ve ilişkilendirilmiş OnAudioFocusChangeListener öğesinin kaydını iptal eder. Geçici odaklama isteğinde bulunduysanız bu işlem, duraklatan veya kısılmış bir uygulamaya ses çalmaya devam edebileceğini ya da sesini geri yükleyebileceğini bildirir.

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

Bir uygulama ses odağı elde ettiğinde, başka bir uygulama kendisi için ses odağı isteğinde bulunduğunda bu uygulamayı serbest bırakabilmelidir. Bu durumda uygulamanız, requestAudioFocus() adlı uygulamayı çağırırken belirttiğiniz AudioFocusChangeListener öğesindeki onAudioFocusChange() yöntemine çağrı alır.

onAudioFocusChange() öğesine iletilen focusChange parametresi, gerçekleşen değişikliğin türünü belirtir. Odaklanmayı sağlayan uygulama tarafından kullanılan süre ipucuna karşılık gelir. Uygulamanız da 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ın eğilmesi (otomatik kısma kullanmıyorsanız) ya da oynamayı duraklatması ancak aksi takdirde aynı durumu koruması gerekir.

Ses odağının geçici olarak kaybolması sırasında ses odağındaki değişiklikleri izlemeye devam etmeli ve yeniden odaklandığınızda normal oynatmaya devam etmeye hazır olmalısınız. Engelleme uygulaması odaklanmayı bıraktığında geri arama (AUDIOFOCUS_GAIN) alırsınız. Bu noktada ses düzeyini normal seviyesine geri getirebilir veya oynatmayı yeniden başlatabilirsiniz.

Kalıcı odak kaybı
Ses odağı kaybı kalıcısa (AUDIOFOCUS_LOSS) başka bir uygulama ses çalıyordur. Uygulamanız, hiçbir zaman AUDIOFOCUS_GAIN araması 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 oynat aktarma kontrolüne basmak 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ğının kalıcı olarak kaybolması durumunda geri çağırmayı ertelemek 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 benzeyen 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 durum değişikliklerine yanıt olarak mHandler.removeCallbacks(mDelayedStopRunnable) yöntemini çağırın. Örneğin, Geri Arama'nın onPlay(), onSkipToNext() vb. öğelerinde removeCallbacks() yöntemini çağırın. Hizmetiniz tarafından kullanılan kaynakları temizlerken hizmetinizin onDestroy() geri çağırmasında da bu yöntemi çağırmalısınız.