Uno degli usi più popolari per i dispositivi mobili è la riproduzione audio attraverso servizi di streaming musicale, podcast scaricati o qualsiasi altro numero di sorgenti audio. Anche se questa è una caratteristica abbastanza comune, è difficile da implementare, con molti pezzi diversi che devono essere costruiti correttamente per offrire all'utente un'esperienza completa Android.
In questo tutorial imparerai a conoscere MediaSessionCompat
dalla libreria di supporto Android e come può essere utilizzato per creare un adeguato servizio di audio di sottofondo per i tuoi utenti.
La prima cosa che devi fare è includere la libreria di supporto Android nel tuo progetto. Questo può essere fatto aggiungendo la seguente riga nel modulo build.gradle file sotto il nodo delle dipendenze.
compila "com.android.support:support-v13:24.2.1"
Dopo aver sincronizzato il tuo progetto, crea una nuova classe Java. Per questo esempio chiamerò la classe BackgroundAudioService
. Questa classe dovrà essere estesa MediaBrowserServiceCompat
. Implementeremo inoltre le seguenti interfacce: MediaPlayer.OnCompletionListener
e AudioManager.OnAudioFocusChangeListener
.
Ora che sei tuo MediaBrowserServiceCompat
l'implementazione è stata creata, prendiamoci un momento per l'aggiornamento AndroidManifest.xml prima di tornare in questa classe. Nella parte superiore della classe, dovrai richiedere il WAKE_LOCK
autorizzazione.
Successivamente, all'interno del applicazione
nodo, dichiara il tuo nuovo servizio con quanto segue intent-filtro
elementi. Ciò consentirà al servizio di intercettare i pulsanti di controllo, gli eventi delle cuffie e la navigazione multimediale per dispositivi, come Android Auto (anche se non eseguiremo nulla con Android Auto per questo tutorial, è ancora richiesto un supporto di base per MediaBrowserServiceCompat
).
Infine, dovrai dichiarare l'uso di MediaButtonReceiver
dalla libreria di supporto Android. Ciò consentirà di intercettare le interazioni dei pulsanti di controllo multimediale e gli eventi delle cuffie sui dispositivi che eseguono KitKat e versioni precedenti.
Ora che sei tuo AndroidManifest.xml il file è finito, puoi chiuderlo. Creeremo anche un'altra classe chiamata MediaStyleHelper
, che è stato scritto da Ian Lake, Developer Advocate di Google, per ripulire la creazione di notifiche di stile dei media.
public class MediaStyleHelper / ** * Crea una notifica utilizzando le informazioni della sessione multimediale specificata. Fa un uso intensivo * di @link MediaMetadataCompat # getDescription () per estrarre le informazioni appropriate. * Contesto @param Contesto utilizzato per costruire la notifica. * @param mediaSession Sessione multimediale per ottenere informazioni. * @return Una notifica pre-compilata con informazioni dalla sessione media specificata. * / public static NotificationCompat.Builder from (Context context, MediaSessionCompat mediaSession) MediaControllerCompat controller = mediaSession.getController (); MediaMetadataCompat mediaMetadata = controller.getMetadata (); MediaDescriptionCompat description = mediaMetadata.getDescription (); NotificationCompat.Builder builder = new NotificationCompat.Builder (context); builder .setContentTitle (description.getTitle ()) .setContentText (description.getSubtitle ()) .setSubText (description.getDescription ()) .setLargeIcon (description.getIconBitmap ()) .setContentIntent (controller.getSessionActivity ()) .setDeleteIntent (MediaButtonReceiver .buildMediaButtonPendingIntent (context, PlaybackStateCompat.ACTION_STOP)) .setVisibility (NotificationCompat.VISIBILITY_PUBLIC); costruttore di ritorno;
Una volta creato, vai avanti e chiudi il file. Ci concentreremo sul servizio audio in background nella prossima sezione.
Ora è il momento di scavare nel cuore della creazione della tua app multimediale. Ci sono alcune variabili membro che vorrete dichiarare per prime per questa app di esempio: a Media Player
per la riproduzione effettiva e a MediaSessionCompat
oggetto che gestirà metadati e controlli / stati di riproduzione.
MediaPlayer mMediaPlayer privato; private MediaSessionCompat mMediaSessionCompat;
Inoltre, avrai bisogno di un BroadcastReceiver
che ascolta i cambiamenti nello stato delle cuffie. Per mantenere le cose semplici, questo ricevitore metterà in pausa il Media Player
, se sta giocando.
private BroadcastReceiver mNoisyReceiver = new BroadcastReceiver () @Override public void onReceive (Context context, Intent intent) if (mMediaPlayer! = null && mMediaPlayer.isPlaying ()) mMediaPlayer.pause (); ;
Per la variabile membro finale, dovrai creare un MediaSessionCompat.Callback
oggetto, che viene utilizzato per gestire lo stato di riproduzione quando si verificano azioni della sessione multimediale.
private MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback () @Override public void onPlay () super.onPlay (); @Override public void onPause () super.onPause (); @Override public void onPlayFromMediaId (String mediaId, Bundle extras) super.onPlayFromMediaId (mediaId, extra); ;
Rivedremo ciascuno dei metodi sopra riportati più avanti in questo tutorial, poiché verranno utilizzati per guidare le operazioni nella nostra app multimediale.
Ci sono due metodi che dovremo anche dichiarare, anche se non avranno bisogno di fare nulla per gli scopi di questo tutorial: onGetRoot ()
e onLoadChildren ()
. È possibile utilizzare il seguente codice per i valori predefiniti.
@Nullable @Override public BrowserRoot onGetRoot (@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) if (TextUtils.equals (clientPackageName, getPackageName ())) return new BrowserRoot (getString (R.string.app_name), null ); return null; // Non importante per il servizio audio generale, richiesto per la classe @Override public void onLoadChildren (@NonNull String parentId, @NonNull Result> risultato) result.sendResult (null);
Infine, vorrai scavalcare il onStartCommand ()
metodo, che è il punto di ingresso nel tuo Servizio
. Questo metodo prenderà l'Intento che viene passato al Servizio
e inviarlo al MediaButtonReceiver
classe.
@Override public int onStartCommand (Intent intent, int flags, int startId) MediaButtonReceiver.handleIntent (mMediaSessionCompat, intent); return super.onStartCommand (intent, flags, startId);
Ora che le variabili dei membri di base sono state create, è ora di inizializzare tutto. Lo faremo chiamando diversi metodi di supporto in onCreate ()
.
@Override public void onCreate () super.onCreate (); initMediaPlayer (); initMediaSession (); initNoisyReceiver ();
Il primo metodo, initMediaPlayer ()
, inizializzerà il Media Player
oggetto che abbiamo creato all'inizio della classe, richiede il blocco parziale della sveglia (che è il motivo per cui abbiamo richiesto tale autorizzazione AndroidManifest.xml) e imposta il volume del lettore.
private void initMediaPlayer () mMediaPlayer = new MediaPlayer (); mMediaPlayer.setWakeMode (getApplicationContext (), PowerManager.PARTIAL_WAKE_LOCK); mMediaPlayer.setAudioStreamType (AudioManager.STREAM_MUSIC); mMediaPlayer.setVolume (1.0f, 1.0f);
Il prossimo metodo, initMediaSession ()
, è dove inizializziamo il MediaSessionCompat
oggetti e collegalo ai pulsanti multimediali e ai metodi di controllo che ci consentono di gestire la riproduzione e l'input dell'utente. Questo metodo inizia creando un Nome del componente
oggetto che punta alla libreria di supporto Android MediaButtonReceiver
classe, e usa quello per creare un nuovo MediaSessionCompat
. Passiamo quindi al MediaSession.Callback
oggetto che abbiamo creato in precedenza e impostare i flag necessari per ricevere gli input dei pulsanti multimediali e i segnali di controllo. Successivamente, creiamo un nuovo Intento
per gestire gli input dei pulsanti multimediali sui dispositivi pre-Lollipop e impostare il token di sessione multimediale per il nostro servizio.
private void initMediaSession () ComponentName mediaButtonReceiver = new ComponentName (getApplicationContext (), MediaButtonReceiver.class); mMediaSessionCompat = new MediaSessionCompat (getApplicationContext (), "Tag", mediaButtonReceiver, null); mMediaSessionCompat.setCallback (mMediaSessionCallback); mMediaSessionCompat.setFlags (MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); Inten mediaButtonIntent = new Intent (Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.setClass (this, MediaButtonReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast (this, 0, mediaButtonIntent, 0); mMediaSessionCompat.setMediaButtonReceiver (pendingIntent); setSessionToken (mMediaSessionCompat.getSessionToken ());
Infine, registreremo il BroadcastReceiver
che abbiamo creato al vertice della classe in modo che possiamo ascoltare gli eventi di cambiamento delle cuffie.
private nude initNoisyReceiver () // Gestisce le cuffie che vengono scollegate. non può essere eseguito tramite un ricevitore manifest Filtro IntentFilter = new IntentFilter (AudioManager.ACTION_AUDIO_BECOMING_NOISY); registerReceiver (mNoisyReceiver, filter);
Ora che hai finito di inizializzare BroadcastReceiver
, MediaSessionCompat
e Media Player
oggetti, è il momento di esaminare la gestione dell'audio focus.
Mentre possiamo pensare che le nostre app audio siano le più importanti al momento, altre app sul dispositivo saranno in competizione per creare i propri suoni, come una notifica via email o un gioco mobile. Per lavorare con queste diverse situazioni, il sistema Android utilizza l'audio focus per determinare come deve essere gestito l'audio.
Il primo caso che vorremmo gestire è l'avvio della riproduzione e il tentativo di ricevere il focus del dispositivo. Nel tuo MediaSessionCompat.Callback
oggetto, vai nel onPlay ()
metodo e aggiungere il seguente controllo delle condizioni.
@Override public void onPlay () super.onPlay (); if (! successfullyRetrievedAudioFocus ()) return;
Il codice sopra richiamerà un metodo di supporto che tenta di recuperare lo stato attivo e, se non può, restituirà semplicemente. In una vera app, si vorrebbe gestire la riproduzione audio fallita più elegantemente. successfullyRetrievedAudioFocus ()
otterrà un riferimento al sistema AudioManager
, e tentare di richiedere l'audio focus per lo streaming di musica. Restituirà quindi a booleano
che rappresenta se la richiesta è riuscita o meno.
private boolean successfullyRetrievedAudioFocus () AudioManager audioManager = (AudioManager) getSystemService (Context.AUDIO_SERVICE); int result = audioManager.requestAudioFocus (questo, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); risultato restituito == AudioManager.AUDIOFOCUS_GAIN;
Noterai che stiamo anche passando Questo
nel requestAudioFocus ()
metodo, che associa il OnAudioFocusChangeListener
con il nostro servizio. Ci sono alcuni stati diversi che vorresti ascoltare per essere un "buon cittadino" nell'ecosistema delle app del dispositivo.
AudioManager.AUDIOFOCUS_LOSS
: Questo si verifica quando un'altra app ha richiesto il focus audio. Quando ciò accade, devi interrompere la riproduzione audio nella tua app.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
: Questo stato viene inserito quando un'altra app desidera riprodurre audio, ma prevede solo la necessità di mettere a fuoco per un breve periodo. È possibile utilizzare questo stato per mettere in pausa la riproduzione audio.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
: Quando viene richiesta la messa a fuoco dell'audio, ma genera uno stato "può anatra", significa che è possibile continuare la riproduzione, ma dovrebbe abbassare leggermente il volume. Ciò può verificarsi quando un suono di notifica viene riprodotto dal dispositivo.AudioManager.AUDIOFOCUS_GAIN
: Lo stato finale di cui parleremo è AUDIOFOCUS_GAIN
. Questo è lo stato in cui è stata completata la riproduzione audio abbordabile e la tua app può riprendere ai livelli precedenti.A semplificato onAudioFocusChange ()
la richiamata può apparire come questa:
@Override public void onAudioFocusChange (int focusChange) switch (focusChange) case AudioManager.AUDIOFOCUS_LOSS: if (mMediaPlayer.isPlaying ()) mMediaPlayer.stop (); rompere; caso AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: mMediaPlayer.pause (); rompere; caso AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: if (mMediaPlayer! = null) mMediaPlayer.setVolume (0.3f, 0.3f); rompere; caso AudioManager.AUDIOFOCUS_GAIN: if (mMediaPlayer! = null) if (! mMediaPlayer.isPlaying ()) mMediaPlayer.start (); mMediaPlayer.setVolume (1.0f, 1.0f); rompere;
Ora che hai una struttura generale insieme per il tuo Servizio
, è ora di tuffarsi nel MediaSessionCompat.Callback
. Nell'ultima sezione hai aggiunto un po 'a onPlay ()
per verificare se l'audio focus è stato concesso. Sotto l'istruzione condizionale, vorrai impostare il MediaSessionCompat
oggetto per attivo, dargli uno stato di STATE_PLAYING
, e assegnare le azioni appropriate necessarie per creare pulsanti di pausa sui controlli della schermata di blocco pre-Lollipop, sul telefono e sulle notifiche di Android Wear.
@Override public void onPlay () super.onPlay (); if (! successfullyRetrievedAudioFocus ()) return; mMediaSessionCompat.setActive (true); setMediaPlaybackState (PlaybackStateCompat.STATE_PLAYING); ...
Il setMediaPlaybackState ()
il metodo sopra è un metodo di supporto che crea un PlaybackStateCompat.Builder
oggetto e fornisce le azioni e lo stato corretti, quindi crea e associa a PlaybackStateCompat
con il tuo MediaSessionCompat
oggetto.
private void setMediaPlaybackState (stato int) PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder (); if (state == PlaybackStateCompat.STATE_PLAYING) playbackstateBuilder.setActions (PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE); else playbackstateBuilder.setActions (PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY); playbackstateBuilder.setState (state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0); mMediaSessionCompat.setPlaybackState (playbackstateBuilder.build ());
È importante notare che avrete bisogno di entrambi ACTION_PLAY_PAUSE
e nemmeno ACTION_PAUSE
o ACTION_PLAY
le bandiere nelle tue azioni per ottenere i controlli appropriati su Android Wear.
Di nuovo dentro onPlay ()
, vorrai mostrare una notifica di riproduzione associata al tuo MediaSessionCompat
oggetto usando il MediaStyleHelper
classe che abbiamo definito in precedenza, quindi mostriamo quella notifica.
private void showPlayingNotification () NotificationCompat.Builder builder = MediaStyleHelper.from (BackgroundAudioService.this, mMediaSessionCompat); if (builder == null) return; builder.addAction (new NotificationCompat.Action (android.R.drawable.ic_media_pause, "Pause", MediaButtonReceiver.buildMediaButtonPendingIntent (this, PlaybackStateCompat.ACTION_PLAY_PAUSE))); builder.setStyle (new NotificationCompat.MediaStyle (). setShowActionsInCompactView (0) .setMediaSession (mMediaSessionCompat.getSessionToken ())); builder.setSmallIcon (R.mipmap.ic_launcher); NotificationManagerCompat.from (BackgroundAudioService.this) .notify (1, builder.build ());
Finalmente, inizierai il Media Player
alla fine di onPlay ()
.
@Override public void onPlay () super.onPlay (); ... showPlayingNotification (); mMediaPlayer.start ();
Quando la richiamata riceve un comando di pausa, onPause ()
sarà chiamato. Qui fermerai il Media Player
, imposta lo stato a STATE_PAUSED
, e mostra una notifica in pausa.
@ Override public void onPause () super.onPause (); if (mMediaPlayer.isPlaying ()) mMediaPlayer.pause (); setMediaPlaybackState (PlaybackStateCompat.STATE_PAUSED); showPausedNotification ();
Nostro showPausedNotification ()
il metodo di supporto sarà simile al showPlayNotification ()
metodo.
private void showPausedNotification () NotificationCompat.Builder builder = MediaStyleHelper.from (questo, mMediaSessionCompat); if (builder == null) return; builder.addAction (new NotificationCompat.Action (android.R.drawable.ic_media_play, "Play", MediaButtonReceiver.buildMediaButtonPendingIntent (this, PlaybackStateCompat.ACTION_PLAY_PAUSE))); builder.setStyle (new NotificationCompat.MediaStyle (). setShowActionsInCompactView (0) .setMediaSession (mMediaSessionCompat.getSessionToken ())); builder.setSmallIcon (R.mipmap.ic_launcher); NotificationManagerCompat.from (this) .notify (1, builder.build ());
Il prossimo metodo nel callback di cui parleremo, onPlayFromMediaId ()
, prende un Stringa
e a impacchettare
come parametri. Questo è il metodo di callback che puoi utilizzare per modificare tracce audio / contenuti all'interno della tua app.
Per questo tutorial, accetteremo semplicemente un ID risorsa grezzo e tenteremo di riprodurlo, quindi reinizializzeremo i metadati della sessione. Come ti è permesso passare un impacchettare
in questo metodo, è possibile utilizzarlo per personalizzare altri aspetti della riproduzione multimediale, ad esempio impostando un suono di sottofondo personalizzato per una traccia.
@Override public void onPlayFromMediaId (String mediaId, Bundle extras) super.onPlayFromMediaId (mediaId, extra); prova AssetFileDescriptor afd = getResources (). openRawResourceFd (Integer.valueOf (mediaId)); if (afd == null) return; prova mMediaPlayer.setDataSource (afd.getFileDescriptor (), afd.getStartOffset (), afd.getLength ()); catch (IllegalStateException e) mMediaPlayer.release (); initMediaPlayer (); mMediaPlayer.setDataSource (afd.getFileDescriptor (), afd.getStartOffset (), afd.getLength ()); afd.close (); initMediaSessionMetadata (); catch (IOException e) return; prova mMediaPlayer.prepare (); catch (IOException e) // Lavora con gli extra qui se vuoi
Ora che abbiamo discusso i due metodi principali in questo callback che utilizzerai nelle tue app, è importante sapere che esistono altri metodi facoltativi che puoi usare per personalizzare il tuo servizio. Alcuni metodi includono onSeekTo ()
, che ti permette di cambiare la posizione di riproduzione del tuo contenuto, e a comando()
, che accetterà un Stringa
denotando il tipo di comando, a impacchettare
per ulteriori informazioni sul comando e a ResultReceiver
callback, che ti permetterà di inviare comandi personalizzati al tuo Servizio
.
@Override public void onCommand (comando String, Extra bundle, ResultReceiver cb) super.onCommand (command, extras, cb); if (COMMAND_EXAMPLE.equalsIgnoreCase (comando)) // comando personalizzato qui @Override public void onSeekTo (long pos) super.onSeekTo (pos);
Quando il nostro file audio sarà completato, vorremmo decidere quale sarà la nostra prossima azione. Mentre potresti voler riprodurre la traccia successiva nella tua app, manterremo le cose semplici e rilasciamo il Media Player
.
@Override public void onCompletion (MediaPlayer mediaPlayer) if (mMediaPlayer! = Null) mMediaPlayer.release ();
Infine, vorremmo fare alcune cose nel OnDestroy ()
metodo del nostro Servizio
. Innanzitutto, ottenere un riferimento al servizio di sistema AudioManager
, e chiama abandonAudioFocus ()
con la nostra AudioFocusChangeListener
come parametro, che notificherà ad altre app sul dispositivo che stai abbandonando l'audio focus. Quindi, annullare la registrazione di BroadcastReceiver
che è stato impostato per ascoltare le modifiche delle cuffie e rilasciare il MediaSessionCompat
oggetto. Infine, si desidera annullare la notifica del controllo di riproduzione.
@Override public void onDestroy () super.onDestroy (); AudioManager audioManager = (AudioManager) getSystemService (Context.AUDIO_SERVICE); audioManager.abandonAudioFocus (questo); unregisterReceiver (mNoisyReceiver); mMediaSessionCompat.release (); NotificationManagerCompat.from (questo) .CANCEL (1);
A questo punto, dovresti avere un audio di base di base funzionante Servizio
utilizzando MediaSessionCompat
per il controllo della riproduzione su tutti i dispositivi. Sebbene ci sia già stato molto impegno nella creazione del servizio, dovresti essere in grado di controllare la riproduzione dalla tua app, una notifica, i controlli della schermata di blocco sui dispositivi pre-Lollipop (Lollipop e versioni successive utilizzeranno la notifica sulla schermata di blocco), e dai dispositivi periferici, come Android Wear, una volta il Servizio
è iniziato.
Mentre la maggior parte dei controlli sarà automatica, avrai ancora un po 'di lavoro per avviare e controllare una sessione multimediale dai tuoi controlli in-app. Per lo meno, vorrete un MediaBrowserCompat.ConnectionCallback
, MediaControllerCompat.Callback
, MediaBrowserCompat
, e MediaControllerCompat
oggetti creati nella tua app.
MediaControllerCompat.Callback
avrà un metodo chiamato onPlaybackStateChanged ()
che riceve modifiche nello stato di riproduzione e può essere utilizzato per mantenere la tua interfaccia utente in sincronia.
private MediaControllerCompat.Callback mMediaControllerCompatCallback = new MediaControllerCompat.Callback () @Override public void onPlaybackStateChanged (stato PlaybackStateCompat) super.onPlaybackStateChanged (stato); if (state == null) return; switch (state.getState ()) case PlaybackStateCompat.STATE_PLAYING: mCurrentState = STATE_PLAYING; rompere; caso PlaybackStateCompat.STATE_PAUSED: mCurrentState = STATE_PAUSED; rompere; ;
MediaBrowserCompat.ConnectionCallback
ha un onConnected ()
metodo che verrà chiamato quando un nuovo MediaBrowserCompat
l'oggetto è creato e connesso. Puoi usare questo per inizializzare il tuo MediaControllerCompat
oggetto, collegalo al tuo MediaControllerCompat.Callback
, e associarlo con MediaSessionCompat
dal tuo Servizio
. Una volta completato, è possibile avviare la riproduzione audio da questo metodo.
private MediaBrowserCompat.ConnectionCallback mMediaBrowserCompatConnectionCallback = new MediaBrowserCompat.ConnectionCallback () @Override public void onConnected () super.onConnected (); try mMediaControllerCompat = new MediaControllerCompat (MainActivity.this, mMediaBrowserCompat.getSessionToken ()); mMediaControllerCompat.registerCallback (mMediaControllerCompatCallback); setSupportMediaController (mMediaControllerCompat); getSupportMediaController (). getTransportControls (). playFromMediaId (String.valueOf (R.raw.warner_tautz_off_broadway), null); catch (RemoteException e) ;
Noterai che lo snippet di codice sopra riportato viene utilizzato getSupportMediaController (). getTransportControls ()
per comunicare con la sessione multimediale. Usando la stessa tecnica, puoi chiamare onPlay ()
e onPause ()
nel tuo servizio audio MediaSessionCompat.Callback
oggetto.
if (mCurrentState == STATE_PAUSED) getSupportMediaController (). getTransportControls (). play (); mCurrentState = STATE_PLAYING; else if (getSupportMediaController (). getPlaybackState (). getState () == PlaybackStateCompat.STATE_PLAYING) getSupportMediaController (). getTransportControls (). pause (); mCurrentState = STATE_PAUSED;
Quando hai finito con la riproduzione audio, puoi mettere in pausa il servizio audio e disconnetterti MediaBrowserCompat
oggetto, che faremo in questo tutorial quando questo Attività
è distrutto.
@Override protected void onDestroy () super.onDestroy (); if (getSupportMediaController (). getPlaybackState (). getState () == PlaybackStateCompat.STATE_PLAYING) getSupportMediaController (). getTransportControls (). pause (); mMediaBrowserCompat.disconnect ();
Meno male! Come puoi vedere, ci sono molti pezzi in movimento coinvolti nella creazione e nell'utilizzo corretto di un servizio audio in background.
In questo tutorial, hai creato un servizio che riproduce un semplice file audio, ascolta le modifiche apportate alla messa a fuoco audio e ai collegamenti MediaSessionCompat
per fornire un controllo di riproduzione universale su dispositivi Android, inclusi telefoni e Android Wear. Se ti imbatti in roadblock mentre lavori attraverso questo tutorial, ti consiglio vivamente di verificare il codice del progetto Android associato sul GitHub di Envato Tuts +.
E dai un'occhiata ad alcuni dei nostri altri corsi e tutorial su Android qui su Envato Tuts+!