Gestire la messa a fuoco audio

Due o più app per Android possono riprodurre l'audio nello stesso stream di output contemporaneamente e il sistema combina tutto. Sebbene questo sia tecnicamente impressionante, può essere molto fastidioso per un utente. Per evitare che tutte le app musicali vengano riprodotte contemporaneamente, Android introduce il concetto di focus audio. Solo un'app alla volta può avere il controllo dell'audio.

Quando l'app deve riprodurre l'audio, deve richiedere l'audio focus. Quando ha lo stato attivo, può riprodurre l'audio. Tuttavia, dopo aver acquisito lo stato attivo dell'audio, potresti non essere in grado di mantenerlo fino al termine della riproduzione. Un'altra app può richiedere il focus, il che preclude il tuo controllo sul focus audio. In questo caso, l'app deve mettere in pausa la riproduzione o abbassare il volume per consentire agli utenti di ascoltare più facilmente la nuova sorgente audio.

Prima di Android 12 (livello API 31), l'audio focus non è gestito dal sistema. Pertanto, sebbene gli sviluppatori di app siano incoraggiati a rispettare le linee guida per l'audio focus, se un'app continua a riprodurre l'audio ad alto volume anche dopo aver perso l'audio focus su un dispositivo con Android 11 (livello API 30) o versioni precedenti, il sistema non può impedirlo. Tuttavia, questo comportamento dell'app genera un'esperienza utente negativa e spesso può indurre gli utenti a disinstallare l'app che si comporta in modo anomalo.

Un'app audio ben progettata deve gestire l'audio focus in base a queste linee guida generali:

  • Chiama requestAudioFocus() immediatamente prima di iniziare la riproduzione e verifica che la chiamata restituisca AUDIOFOCUS_REQUEST_GRANTED. Effettua la chiamata a requestAudioFocus() nel callback onPlay() della sessione multimediale.

  • Quando un'altra app ottiene il focus audio, interrompi o metti in pausa la riproduzione oppure abbassa il volume.

  • Quando la riproduzione si interrompe (ad esempio, quando l'app non ha più contenuti da riprodurre), abbandona la priorità audio. La tua app non deve abbandonare il livello di priorità audio se l'utente mette in pausa la riproduzione, ma potrebbe riprenderla in un secondo momento.

  • Utilizza AudioAttributes per descrivere il tipo di audio riprodotto dalla tua app. Ad esempio, per le app che riproducono contenuti vocali, specifica CONTENT_TYPE_SPEECH.

La priorità audio viene gestita in modo diverso a seconda della versione di Android in esecuzione:

Android 12 (livello API 31) o versioni successive
La priorità audio è gestita dal sistema. Il sistema forza la dissolvenza della riproduzione audio di un'app quando un'altra app richiede l'audio focus. Il sistema disattiva anche la riproduzione audio quando viene ricevuta una chiamata in arrivo.
Da Android 8.0 (livello API 26) ad Android 11 (livello API 30)
La selezione audio non è gestita dal sistema, ma include alcune modifiche introdotte a partire da Android 8.0 (livello API 26).
Android 7.1 (livello API 25) e versioni precedenti
La selezione audio non è gestita dal sistema e le app la gestiscono utilizzando requestAudioFocus() e abandonAudioFocus().

Focalizzazione audio in Android 12 e versioni successive

Un'app di contenuti multimediali o di gioco che utilizza il focus audio non deve riprodurre audio dopo aver perso il focus. In Android 12 (livello API 31) e versioni successive, il sistema applica questo comportamento. Quando un'app richiede l'audio focus mentre un'altra app ha il focus e sta riproducendo, il sistema forza la dissolvenza dell'app in riproduzione. L'aggiunta della dissolvenza in uscita offre una transizione più fluida quando si passa da un'app all'altra.

Questo comportamento di dissolvenza si verifica quando sono soddisfatte le seguenti condizioni:

  1. La prima app, quella attualmente in riproduzione, soddisfa tutti i seguenti criteri:

  2. Una seconda app richiede l'audio focus con AudioManager.AUDIOFOCUS_GAIN.

Quando queste condizioni sono soddisfatte, l'impianto audio applica la dissolvenza in uscita alla prima app. Al termine della dissolvenza in uscita, il sistema comunica alla prima app la perdita di focus. I player dell'app rimangono disattivati finché l'app non richiede nuovamente l'audio focus.

Comportamenti di focus audio esistenti

Devi anche tenere presente questi altri casi che comportano un cambio di focus audio.

Attenuazione automatica audio

L'attenuazione automatica (riduzione temporanea del livello audio di un'app in modo che un'altra possa essere ascoltata chiaramente) è stata introdotta in Android 8.0 (livello API 26).

Se il sistema implementa l'attenuazione, non è necessario implementarla nella tua app.

L'attenuazione automatica si verifica anche quando una notifica audio acquisisce lo stato attivo da un'app in riproduzione. L'inizio della riproduzione della notifica è sincronizzato con la fine della rampa di attenuazione.

L'attenuazione automatica si verifica quando sono soddisfatte le seguenti condizioni:

  1. La prima app, attualmente in riproduzione, soddisfa tutti i seguenti criteri:

  2. Una seconda app richiede l'audio focus con AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Quando queste condizioni sono soddisfatte, il sistema audio abbassa il volume di tutti i player attivi della prima app mentre la seconda app è attiva. Quando la seconda app abbandona il focus, il volume viene ripristinato. La prima app non riceve una notifica quando perde lo stato attivo, quindi non deve fare nulla.

Tieni presente che l'attenuazione automatica non viene eseguita quando l'utente ascolta contenuti vocali, perché potrebbe perdersi parte del programma. Ad esempio, le indicazioni stradali della guida vocale non vengono attenuate.

Disattiva l'audio della riproduzione corrente per le chiamate in arrivo

Alcune app non funzionano correttamente e continuano a riprodurre l'audio durante le telefonate. Questa situazione costringe l'utente a trovare e disattivare l'audio o chiudere l'app che causa il problema per poter ascoltare la chiamata. Per evitare questo problema, il sistema può disattivare l'audio di altre app durante una chiamata in arrivo. Il sistema richiama questa funzionalità quando viene ricevuta una chiamata in arrivo e un'app soddisfa queste condizioni:

  • L'app ha l'attributo di utilizzo AudioAttributes.USAGE_MEDIA o AudioAttributes.USAGE_GAME.
  • L'app ha richiesto correttamente l'audio focus (qualsiasi guadagno di focus) e sta riproducendo audio.

Se un'app continua la riproduzione durante la chiamata, l'audio viene disattivato fino al termine della chiamata. Tuttavia, se un'app avvia la riproduzione durante la chiamata, l'audio del player non viene disattivato, presupponendo che l'utente abbia avviato la riproduzione intenzionalmente.

Focus audio in Android 8.0-Android 11

A partire da Android 8.0 (livello API 26), quando chiami requestAudioFocus() devi fornire un parametro AudioFocusRequest. AudioFocusRequest contiene informazioni sul contesto audio e sulle funzionalità della tua app. Il sistema utilizza queste informazioni per gestire automaticamente l'acquisizione e la perdita del focus audio. Per rilasciare il focus audio, chiama il metodo abandonAudioFocusRequest() che accetta anche un AudioFocusRequest come argomento. Utilizza la stessa istanza AudioFocusRequest sia quando richiedi sia quando abbandoni lo stato attivo.

Per creare un AudioFocusRequest, utilizza un AudioFocusRequest.Builder. Poiché una richiesta di messa a fuoco deve sempre specificare il tipo di richiesta, il tipo è incluso nel costruttore del builder. Utilizza i metodi del builder per impostare gli altri campi della richiesta.

Il campo FocusGain è obbligatorio, mentre tutti gli altri campi sono facoltativi.

MetodoNote
setFocusGain() Questo campo è obbligatorio in ogni richiesta. Assume gli stessi valori di durationHint utilizzati nella chiamata precedente ad Android 8.0 a requestAudioFocus(): AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK o AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
setAudioAttributes() AudioAttributes descrive il caso d'uso della tua app. Il sistema li esamina quando un'app acquisisce e perde l'audio focus. Gli attributi sostituiscono la nozione di tipo di stream. In Android 8.0 (livello API 26) e versioni successive, i tipi di stream per qualsiasi operazione diversa dai controlli del volume sono deprecati. Utilizza nella richiesta di selezione gli stessi attributi che utilizzi nel tuo lettore audio (come mostrato nell'esempio che segue questa tabella).

Utilizza un AudioAttributes.Builder per specificare prima gli attributi, quindi utilizza questo metodo per assegnarli alla richiesta.

Se non specificato, il valore predefinito di AudioAttributes è AudioAttributes.USAGE_MEDIA.

setWillPauseWhenDucked() Quando un'altra app richiede lo stato attivo con AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, l'app che ha lo stato attivo non riceve in genere un callback onAudioFocusChange() perché il sistema può eseguire il ducking autonomamente. Quando devi mettere in pausa la riproduzione anziché abbassare il volume, chiama setWillPauseWhenDucked(true) e crea e imposta un OnAudioFocusChangeListener, come descritto in Abbassamento automatico del volume.
setAcceptsDelayedFocusGain() Una richiesta di selezione audio può non riuscire quando la selezione è bloccata da un'altra app. Questo metodo consente di ritardare l'acquisizione della selezione: la possibilità di acquisire la selezione in modo asincrono quando diventa disponibile.

Tieni presente che l'acquisizione ritardata del focus funziona solo se specifichi anche un AudioManager.OnAudioFocusChangeListener nella richiesta audio, poiché l'app deve ricevere il callback per sapere che il focus è stato concesso.

setOnAudioFocusChangeListener() Un OnAudioFocusChangeListener è obbligatorio solo se specifichi anche willPauseWhenDucked(true) o setAcceptsDelayedFocusGain(true) nella richiesta.

Esistono due metodi per impostare il listener: uno con e uno senza un argomento handler. Il gestore è il thread su cui viene eseguito il listener. Se non specifichi un gestore, viene utilizzato quello associato al Looper principale.

L'esempio seguente mostra come utilizzare un AudioFocusRequest.Builder per creare un AudioFocusRequest e richiedere e abbandonare il focus 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;
        }
    }
}

Attenuazione automatica audio

In Android 8.0 (livello API 26), quando un'altra app richiede l'attenzione con AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, il sistema può abbassare e ripristinare il volume senza richiamare il callback onAudioFocusChange() dell'app.

Sebbene l'attenuazione automatica sia un comportamento accettabile per le app di riproduzione di musica e video, non è utile durante la riproduzione di contenuti parlati, ad esempio in un'app di audiolibri. In questo caso, l'app dovrebbe invece essere messa in pausa.

Se vuoi che la tua app venga messa in pausa quando viene richiesto l'abbassamento del volume anziché diminuire il volume, crea un OnAudioFocusChangeListener con un metodo di callback onAudioFocusChange() che implementi il comportamento di pausa/ripresa desiderato. Chiama setOnAudioFocusChangeListener() per registrare il listener e chiama setWillPauseWhenDucked(true) per indicare al sistema di utilizzare il tuo callback anziché eseguire l'attenuazione automatica.

Guadagno di messa a fuoco ritardato

A volte il sistema non può concedere una richiesta di selezione dell'audio perché la selezione è "bloccata" da un'altra app, ad esempio durante una telefonata. In questo caso, requestAudioFocus() restituisce AUDIOFOCUS_REQUEST_FAILED. In questo caso, l'app non deve procedere con la riproduzione audio perché non ha ottenuto lo stato attivo.

Il metodo setAcceptsDelayedFocusGain(true), che consente alla tua app di gestire una richiesta di selezione in modo asincrono. Con questo flag impostato, una richiesta effettuata quando la messa a fuoco è bloccata restituisce AUDIOFOCUS_REQUEST_DELAYED. Quando la condizione che ha bloccato l'audio focus non esiste più, ad esempio quando termina una telefonata, il sistema concede la richiesta di focus in attesa e chiama onAudioFocusChange() per inviare una notifica alla tua app.

Per gestire l'acquisizione ritardata dello stato attivo, devi creare un OnAudioFocusChangeListener con un metodo di callback onAudioFocusChange() che implementi il comportamento desiderato e registri il listener chiamando setOnAudioFocusChangeListener().

Focus audio in Android 7.1 e versioni precedenti

Quando chiami requestAudioFocus() devi specificare un suggerimento di durata, che potrebbe essere rispettato da un'altra app che attualmente ha lo stato attivo e sta riproducendo contenuti:

  • Richiedi l'audio focus permanente (AUDIOFOCUS_GAIN) se prevedi di riprodurre l'audio per un periodo di tempo prevedibile (ad esempio, quando riproduci musica) e ti aspetti che il precedente titolare dell'audio focus interrompa la riproduzione.
  • Richiedi il focus temporaneo (AUDIOFOCUS_GAIN_TRANSIENT) quando prevedi di riprodurre l'audio solo per un breve periodo di tempo e prevedi che il titolare precedente metta in pausa la riproduzione.
  • Richiedi il focus temporaneo con ducking (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) per indicare che prevedi di riprodurre l'audio solo per un breve periodo di tempo e che il proprietario del focus precedente può continuare a riprodurre se "abbassa" (riduce) l'output audio. Entrambe le uscite audio vengono mixate nello stream audio. Il ducking è particolarmente adatto per le app che utilizzano lo stream audio in modo intermittente, ad esempio per le indicazioni stradali vocali.

Il metodo requestAudioFocus() richiede anche un AudioManager.OnAudioFocusChangeListener. Questo listener deve essere creato nella stessa attività o nello stesso servizio proprietario della sessione multimediale. Implementa il callback onAudioFocusChange() che la tua app riceve quando un'altra app acquisisce o abbandona il focus audio.

Il seguente snippet richiede il focus audio permanente sullo stream STREAM_MUSIC e registra un OnAudioFocusChangeListener per gestire le successive modifiche al focus audio. (Il listener di modifica è descritto in Risposta a una modifica della priorità 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
}

Al termine della riproduzione, chiama abandonAudioFocus().

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

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

In questo modo, il sistema viene informato che non hai più bisogno dello stato attivo e annulla la registrazione del OnAudioFocusChangeListener associato. Se hai richiesto lo stato attivo temporaneo, un'app che è stata messa in pausa o il cui volume è stato abbassato riceverà una notifica che la informa che può continuare la riproduzione o ripristinare il volume.

Risposta a una modifica della priorità audio

Quando un'app acquisisce il focus audio, deve essere in grado di rilasciarlo quando un'altra app lo richiede per sé. In questo caso, la tua app riceve una chiamata al metodo onAudioFocusChange() in AudioFocusChangeListener che hai specificato quando l'app ha chiamato requestAudioFocus().

Il parametro focusChange passato a onAudioFocusChange() indica il tipo di modifica in corso. Corrisponde al suggerimento sulla durata utilizzato dall'app che sta acquisendo il focus. L'app deve rispondere in modo appropriato.

Perdita temporanea di concentrazione
Se la variazione di stato attivo è temporanea (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK o AUDIOFOCUS_LOSS_TRANSIENT), l'app deve abbassare il volume (se non utilizzi l'abbassamento automatico del volume) o mettere in pausa la riproduzione, ma mantenere lo stesso stato.

Durante una perdita temporanea della selezione audio, devi continuare a monitorare le modifiche alla selezione audio ed essere pronto a riprendere la normale riproduzione quando recuperi la selezione. Quando l'app di blocco abbandona la selezione, ricevi un callback (AUDIOFOCUS_GAIN). A questo punto, puoi ripristinare il volume al livello normale o riavviare la riproduzione.

Perdita permanente della messa a fuoco
Se la perdita del focus audio è permanente (AUDIOFOCUS_LOSS), un'altra app sta riproducendo l'audio. L'app deve mettere immediatamente in pausa la riproduzione, poiché non riceverà mai un callback AUDIOFOCUS_GAIN. Per riavviare la riproduzione, l'utente deve eseguire un'azione esplicita, ad esempio premere il controllo di trasporto di riproduzione in una notifica o nell'interfaccia utente dell'app.

Il seguente snippet di codice mostra come implementare OnAudioFocusChangeListener e il relativo callback onAudioFocusChange(). Nota l'utilizzo di un Handler per ritardare il callback di interruzione in caso di perdita permanente della priorità audio.

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

Il gestore utilizza un Runnable simile al seguente:

Kotlin

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

Java

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

Per assicurarti che l'interruzione ritardata non venga attivata se l'utente riavvia la riproduzione, chiama mHandler.removeCallbacks(mDelayedStopRunnable) in risposta a eventuali modifiche dello stato. Ad esempio, chiama removeCallbacks() nel onPlay(), onSkipToNext() e così via del tuo callback. Devi anche chiamare questo metodo nel callback onDestroy() del tuo servizio durante la pulizia delle risorse utilizzate dal servizio.