Hintergrundaudio in Android mit MediaSessionCompat
German (Deutsch) translation by Federicco Ancie (you can also view the original English article)
Eine der beliebtesten Anwendungen für mobile Geräte ist die Wiedergabe von Audio über Musik-Streaming-Dienste, heruntergeladene Podcasts oder eine beliebige andere Anzahl von Audioquellen. Dies ist zwar eine ziemlich häufige Funktion, ist jedoch schwer zu implementieren, da viele verschiedene Teile korrekt erstellt werden müssen, um Ihrem Benutzer das volle Android-Erlebnis zu bieten.
In diesem Tutorial erfahren Sie mehr über MediaSessionCompat aus der Android-Supportbibliothek und wie Sie damit einen geeigneten Hintergrundaudiodienst für Ihre Benutzer erstellen können.
Einrichten
Als erstes müssen Sie die Android-Supportbibliothek in Ihr Projekt aufnehmen. Dies können Sie tun, indem Sie die folgende Zeile in die Datei build.gradle Ihres Moduls unter dem Knoten Abhängigkeiten einfügen.
1 |
compile 'com.android.support:support-v13:24.2.1' |
Nachdem Sie Ihr Projekt synchronisiert haben, erstellen Sie eine neue Java-Klasse. Für dieses Beispiel nenne ich die Klasse BackgroundAudioService. Diese Klasse muss MediaBrowserServiceCompat erweitern. Außerdem werden wir folgende Schnittstellen implementieren: MediaPlayer.OnCompletionListener und AudioManager.OnAudioFocusChangeListener.
Nachdem Ihre MediaBrowserServiceCompat-Implementierung jetzt erstellt wurde, nehmen wir uns einen Moment Zeit, um AndroidManifest.xml zu aktualisieren, bevor wir zu dieser Klasse zurückkehren. Oben in der Klasse müssen Sie die WAKE_LOCK-Berechtigung anfordern.
1 |
<uses-permission android:name="android.permission.WAKE_LOCK" /> |
Deklarieren Sie als Nächstes innerhalb des application-Knotens Ihren neuen Dienst mit den folgenden intent-filter-Elementen. Dadurch kann Ihr Dienst Steuertasten, Kopfhörerereignisse und das Durchsuchen von Medien für Geräte wie Android Auto abfangen (obwohl wir in diesem Tutorial nichts mit Android Auto tun werden, wird von MediaBrowserServiceCompat dennoch einige grundlegende Unterstützung dafür benötigt).
1 |
<service android:name=".BackgroundAudioService"> |
2 |
<intent-filter>
|
3 |
<action android:name="android.intent.action.MEDIA_BUTTON" /> |
4 |
<action android:name="android.media.AUDIO_BECOMING_NOISY" /> |
5 |
<action android:name="android.media.browse.MediaBrowserService" /> |
6 |
</intent-filter>
|
7 |
</service>
|
Schließlich müssen Sie die Verwendung des MediaButtonReceiver aus der Android-Supportbibliothek deklarieren. Auf diese Weise können Sie Interaktionen mit Mediensteuerungstasten und Kopfhörerereignisse auf Geräten abfangen, auf denen KitKat und früher ausgeführt wird.
1 |
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver"> |
2 |
<intent-filter>
|
3 |
<action android:name="android.intent.action.MEDIA_BUTTON" /> |
4 |
<action android:name="android.media.AUDIO_BECOMING_NOISY" /> |
5 |
</intent-filter>
|
6 |
</receiver>
|
Nachdem Ihre AndroidManifest.xml-Datei fertig ist, können Sie sie schließen. Wir werden auch eine weitere Klasse namens MediaStyleHelper erstellen, die von Ian Lake, Developer Advocate bei Google, geschrieben wurde, um die Erstellung von Medienstilbenachrichtigungen zu bereinigen.
1 |
public class MediaStyleHelper { |
2 |
/**
|
3 |
* Build a notification using the information from the given media session. Makes heavy use
|
4 |
* of {@link MediaMetadataCompat#getDescription()} to extract the appropriate information.
|
5 |
* @param context Context used to construct the notification.
|
6 |
* @param mediaSession Media session to get information.
|
7 |
* @return A pre-built notification with information from the given media session.
|
8 |
*/
|
9 |
public static NotificationCompat.Builder from( |
10 |
Context context, MediaSessionCompat mediaSession) { |
11 |
MediaControllerCompat controller = mediaSession.getController(); |
12 |
MediaMetadataCompat mediaMetadata = controller.getMetadata(); |
13 |
MediaDescriptionCompat description = mediaMetadata.getDescription(); |
14 |
|
15 |
NotificationCompat.Builder builder = new NotificationCompat.Builder(context); |
16 |
builder
|
17 |
.setContentTitle(description.getTitle()) |
18 |
.setContentText(description.getSubtitle()) |
19 |
.setSubText(description.getDescription()) |
20 |
.setLargeIcon(description.getIconBitmap()) |
21 |
.setContentIntent(controller.getSessionActivity()) |
22 |
.setDeleteIntent( |
23 |
MediaButtonReceiver.buildMediaButtonPendingIntent(context, PlaybackStateCompat.ACTION_STOP)) |
24 |
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); |
25 |
return builder; |
26 |
}
|
27 |
}
|
Sobald das erstellt wurde, fahren Sie fort und schließen Sie die Datei. Wir werden uns im nächsten Abschnitt auf den Hintergrundaudiodienst konzentrieren.
Aufbau des Hintergrundaudiodienstes
Jetzt ist es an der Zeit, sich mit dem Kern der Erstellung Ihrer Medien-App zu befassen. Es gibt einige Membervariablen, die Sie zuerst für diese Beispiel-App deklarieren möchten: einen MediaPlayer für die eigentliche Wiedergabe und ein MediaSessionCompat-Objekt, das Metadaten und Wiedergabesteuerelemente/-zustände verwaltet.
1 |
private MediaPlayer mMediaPlayer; |
2 |
private MediaSessionCompat mMediaSessionCompat; |
Außerdem benötigen Sie einen BroadcastReceiver, der auf Änderungen des Kopfhörerstatus lauscht. Der Einfachheit halber hält dieser Receiver den MediaPlayer an, wenn er abgespielt wird.
1 |
private BroadcastReceiver mNoisyReceiver = new BroadcastReceiver() { |
2 |
@Override
|
3 |
public void onReceive(Context context, Intent intent) { |
4 |
if( mMediaPlayer != null && mMediaPlayer.isPlaying() ) { |
5 |
mMediaPlayer.pause(); |
6 |
}
|
7 |
}
|
8 |
};
|
Für die letzte Membervariable erstellen Sie ein MediaSessionCompat.Callback-Objekt, das zum Behandeln des Wiedergabestatus verwendet wird, wenn Mediensitzungsaktionen auftreten.
1 |
private MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() { |
2 |
|
3 |
@Override
|
4 |
public void onPlay() { |
5 |
super.onPlay(); |
6 |
}
|
7 |
|
8 |
@Override
|
9 |
public void onPause() { |
10 |
super.onPause(); |
11 |
}
|
12 |
|
13 |
@Override
|
14 |
public void onPlayFromMediaId(String mediaId, Bundle extras) { |
15 |
super.onPlayFromMediaId(mediaId, extras); |
16 |
}
|
17 |
};
|
Wir werden jede der oben genannten Methoden später in diesem Tutorial wiederholen, da sie verwendet werden, um Vorgänge in unserer Medien-App zu steuern.
Es gibt zwei Methoden, die wir ebenfalls deklarieren müssen, obwohl sie für die Zwecke dieses Tutorials nichts tun müssen: onGetRoot() und onLoadChildren(). Sie können den folgenden Code für Ihre Standardeinstellungen verwenden.
1 |
@Nullable
|
2 |
@Override
|
3 |
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { |
4 |
if(TextUtils.equals(clientPackageName, getPackageName())) { |
5 |
return new BrowserRoot(getString(R.string.app_name), null); |
6 |
}
|
7 |
|
8 |
return null; |
9 |
}
|
10 |
|
11 |
//Not important for general audio service, required for class
|
12 |
@Override
|
13 |
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) { |
14 |
result.sendResult(null); |
15 |
}
|
Schließlich möchten Sie die Methode onStartCommand() überschreiben, die den Einstiegspunkt in Ihren Service darstellt. Diese Methode nimmt den an den Service übergebenen Intent und sendet ihn an die MediaButtonReceiver-Klasse.
1 |
@Override
|
2 |
public int onStartCommand(Intent intent, int flags, int startId) { |
3 |
MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent); |
4 |
return super.onStartCommand(intent, flags, startId); |
5 |
}
|
Alle Dinge initialisieren
Nachdem Ihre Basiselementvariablen erstellt wurden, ist es an der Zeit, alles zu initialisieren. Dazu rufen wir verschiedene Hilfsmethoden in onCreate() auf.
1 |
@Override
|
2 |
public void onCreate() { |
3 |
super.onCreate(); |
4 |
|
5 |
initMediaPlayer(); |
6 |
initMediaSession(); |
7 |
initNoisyReceiver(); |
8 |
}
|
Die erste Methode, initMediaPlayer(), initialisiert das MediaPlayer-Objekt, das wir oben in der Klasse erstellt haben, fordert einen teilweisen Wakelock an (weshalb wir diese Berechtigung in AndroidManifest.xml benötigt haben) und legt die Lautstärke des Players fest.
1 |
private void initMediaPlayer() { |
2 |
mMediaPlayer = new MediaPlayer(); |
3 |
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); |
4 |
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); |
5 |
mMediaPlayer.setVolume(1.0f, 1.0f); |
6 |
}
|
Mit der nächsten Methode, initMediaSession(), initialisieren wir das MediaSessionCompat-Objekt und verbinden es mit den Medienschaltflächen und Steuermethoden, die es uns ermöglichen, Wiedergabe und Benutzereingaben zu verarbeiten. Diese Methode beginnt mit dem Erstellen eines ComponentName-Objekts, das auf die MediaButtonReceiver-Klasse der Android-Supportbibliothek verweist, und verwendet diese zum Erstellen eines neuen MediaSessionCompat. Anschließend übergeben wir das zuvor erstellte MediaSession.Callback-Objekt und setzen die erforderlichen Flags zum Empfangen von Medientasteneingaben und Steuersignalen. Als Nächstes erstellen wir einen neuen Intent für die Verarbeitung von Medientasteneingaben auf Pre-Lollipop-Geräten und legen das Mediensitzungstoken für unseren Dienst fest.
1 |
private void initMediaSession() { |
2 |
ComponentName mediaButtonReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class); |
3 |
mMediaSessionCompat = new MediaSessionCompat(getApplicationContext(), "Tag", mediaButtonReceiver, null); |
4 |
|
5 |
mMediaSessionCompat.setCallback(mMediaSessionCallback); |
6 |
mMediaSessionCompat.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS ); |
7 |
|
8 |
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); |
9 |
mediaButtonIntent.setClass(this, MediaButtonReceiver.class); |
10 |
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0); |
11 |
mMediaSessionCompat.setMediaButtonReceiver(pendingIntent); |
12 |
|
13 |
setSessionToken(mMediaSessionCompat.getSessionToken()); |
14 |
}
|
Schließlich registrieren wir den BroadcastReceiver, den wir oben in der Klasse erstellt haben, damit wir auf Kopfhörerwechselereignisse lauschen können.
1 |
private void initNoisyReceiver() { |
2 |
//Handles headphones coming unplugged. cannot be done through a manifest receiver
|
3 |
IntentFilter filter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); |
4 |
registerReceiver(mNoisyReceiver, filter); |
5 |
}
|
Umgang mit Audiofokus
Nachdem Sie die Initialisierung der BroadcastReceiver-, MediaSessionCompat- und MediaPlayer-Objekte abgeschlossen haben, ist es an der Zeit, sich mit der Handhabung des Audiofokus zu befassen.
Während wir vielleicht denken, dass unsere eigenen Audio-Apps derzeit die wichtigsten sind, konkurrieren andere Apps auf dem Gerät darum, ihre eigenen Sounds zu erzeugen, z. B. eine E-Mail-Benachrichtigung oder ein Handyspiel. Um mit diesen verschiedenen Situationen zu arbeiten, verwendet das Android-System den Audiofokus, um zu bestimmen, wie Audio behandelt werden soll.
Der erste Fall, den wir behandeln möchten, ist das Starten der Wiedergabe und der Versuch, den Fokus des Geräts zu erhalten. Gehen Sie in Ihrem MediaSessionCompat.Callback-Objekt in die Methode onPlay() und fügen Sie die folgende Bedingungsprüfung hinzu.
1 |
@Override
|
2 |
public void onPlay() { |
3 |
super.onPlay(); |
4 |
if( !successfullyRetrievedAudioFocus() ) { |
5 |
return; |
6 |
}
|
7 |
}
|
Der obige Code ruft eine Hilfsmethode auf, die versucht, den Fokus abzurufen, und wenn dies nicht möglich ist, wird einfach zurückgegeben. In einer echten App möchten Sie die fehlgeschlagene Audiowiedergabe eleganter behandeln. successfullyRetrievedAudioFocus() ruft einen Verweis auf den AudioManager des Systems ab und versucht, den Audiofokus für das Streamen von Musik anzufordern. Es gibt dann einen boolean Wert zurück, der angibt, ob die Anforderung erfolgreich war oder nicht.
1 |
private boolean successfullyRetrievedAudioFocus() { |
2 |
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
3 |
|
4 |
int result = audioManager.requestAudioFocus(this, |
5 |
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); |
6 |
|
7 |
return result == AudioManager.AUDIOFOCUS_GAIN; |
8 |
}
|
Sie werden feststellen, dass wir this auch an die Methode requestAudioFocus() übergeben, die den OnAudioFocusChangeListener mit unserem Dienst verknüpft. Es gibt ein paar verschiedene Zustände, auf die Sie achten sollten, um ein "guter Bürger" im App-Ökosystem des Geräts zu sein.
-
AudioManager.AUDIOFOCUS_LOSS: Dies tritt auf, wenn eine andere App den Audiofokus angefordert hat. In diesem Fall sollten Sie die Audiowiedergabe in Ihrer App stoppen. -
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: Dieser Zustand wird erreicht, wenn eine andere App Audio abspielen möchte, aber erwartet, dass sie nur für kurze Zeit fokussiert werden muss. Sie können diesen Status verwenden, um Ihre Audiowiedergabe zu unterbrechen. -
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: Wenn der Audio-Fokus angefordert wird, aber den Status "kann sich ducken" auslöst, bedeutet dies, dass Sie Ihre Wiedergabe fortsetzen können, die Lautstärke jedoch etwas verringern sollten. Dies kann auftreten, wenn ein Benachrichtigungston vom Gerät abgespielt wird. -
AudioManager.AUDIOFOCUS_GAIN: Der letzte Zustand, den wir besprechen werden, istAUDIOFOCUS_GAIN. Dies ist der Zustand, wenn eine Duckable-Audiowiedergabe abgeschlossen ist und Ihre App auf den vorherigen Ebenen fortgesetzt werden kann.
Ein vereinfachter onAudioFocusChange()-Callback kann wie folgt aussehen:
1 |
@Override
|
2 |
public void onAudioFocusChange(int focusChange) { |
3 |
switch( focusChange ) { |
4 |
case AudioManager.AUDIOFOCUS_LOSS: { |
5 |
if( mMediaPlayer.isPlaying() ) { |
6 |
mMediaPlayer.stop(); |
7 |
}
|
8 |
break; |
9 |
}
|
10 |
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: { |
11 |
mMediaPlayer.pause(); |
12 |
break; |
13 |
}
|
14 |
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: { |
15 |
if( mMediaPlayer != null ) { |
16 |
mMediaPlayer.setVolume(0.3f, 0.3f); |
17 |
}
|
18 |
break; |
19 |
}
|
20 |
case AudioManager.AUDIOFOCUS_GAIN: { |
21 |
if( mMediaPlayer != null ) { |
22 |
if( !mMediaPlayer.isPlaying() ) { |
23 |
mMediaPlayer.start(); |
24 |
}
|
25 |
mMediaPlayer.setVolume(1.0f, 1.0f); |
26 |
}
|
27 |
break; |
28 |
}
|
29 |
}
|
30 |
}
|
Den MediaSessionCompat.Callback verstehen
Nachdem Sie nun eine allgemeine Struktur für Ihren Service erstellt haben, ist es an der Zeit, in den MediaSessionCompat.Callback einzutauchen. Im letzten Abschnitt haben Sie onPlay() ein wenig hinzugefügt, um zu überprüfen, ob der Audiofokus gewährt wurde. Unterhalb der bedingten Anweisung möchten Sie das MediaSessionCompat-Objekt auf aktiv setzen, ihm den Status STATE_PLAYING geben und die richtigen Aktionen zuweisen, die zum Erstellen von Pausenschaltflächen für Sperrbildschirm-Steuerelemente vor dem Lollipop, Telefon- und Android Wear-Benachrichtigungen erforderlich sind.
1 |
@Override
|
2 |
public void onPlay() { |
3 |
super.onPlay(); |
4 |
if( !successfullyRetrievedAudioFocus() ) { |
5 |
return; |
6 |
}
|
7 |
|
8 |
mMediaSessionCompat.setActive(true); |
9 |
setMediaPlaybackState(PlaybackStateCompat.STATE_PLAYING); |
10 |
|
11 |
...
|
12 |
}
|
Die obige Methode setMediaPlaybackState() ist eine Hilfsmethode, die ein PlaybackStateCompat.Builder-Objekt erstellt und ihm die richtigen Aktionen und den richtigen Zustand zuweist und dann ein PlaybackStateCompat erstellt und Ihrem MediaSessionCompat-Objekt zuordnet.
1 |
private void setMediaPlaybackState(int state) { |
2 |
PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder(); |
3 |
if( state == PlaybackStateCompat.STATE_PLAYING ) { |
4 |
playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE); |
5 |
} else { |
6 |
playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY); |
7 |
}
|
8 |
playbackstateBuilder.setState(state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0); |
9 |
mMediaSessionCompat.setPlaybackState(playbackstateBuilder.build()); |
10 |
}
|
Beachten Sie, dass Sie in Ihren Aktionen sowohl das ACTION_PLAY_PAUSE- als auch das ACTION_PAUSE- oder ACTION_PLAY-Flag benötigen, um die richtige Steuerung auf Android Wear zu erhalten.



Zurück in onPlay() möchten Sie eine Wiedergabebenachrichtigung anzeigen, die Ihrem MediaSessionCompat-Objekt zugeordnet ist, indem Sie die zuvor definierte MediaStyleHelper-Klasse verwenden, und dann diese Benachrichtigung anzeigen.
1 |
private void showPlayingNotification() { |
2 |
NotificationCompat.Builder builder = MediaStyleHelper.from(BackgroundAudioService.this, mMediaSessionCompat); |
3 |
if( builder == null ) { |
4 |
return; |
5 |
}
|
6 |
|
7 |
|
8 |
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_pause, "Pause", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE))); |
9 |
builder.setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0).setMediaSession(mMediaSessionCompat.getSessionToken())); |
10 |
builder.setSmallIcon(R.mipmap.ic_launcher); |
11 |
NotificationManagerCompat.from(BackgroundAudioService.this).notify(1, builder.build()); |
12 |
}
|
Schließlich starten Sie den MediaPlayer am Ende von onPlay().
1 |
@Override
|
2 |
public void onPlay() { |
3 |
super.onPlay(); |
4 |
|
5 |
...
|
6 |
|
7 |
showPlayingNotification(); |
8 |
mMediaPlayer.start(); |
9 |
}
|



Wenn der Callback einen Pause-Befehl erhält, wird onPause() aufgerufen. Hier pausieren Sie den MediaPlayer, setzen den Status auf STATE_PAUSED und zeigen eine pausierte Benachrichtigung an.
1 |
@Override
|
2 |
public void onPause() { |
3 |
super.onPause(); |
4 |
|
5 |
if( mMediaPlayer.isPlaying() ) { |
6 |
mMediaPlayer.pause(); |
7 |
setMediaPlaybackState(PlaybackStateCompat.STATE_PAUSED); |
8 |
showPausedNotification(); |
9 |
}
|
10 |
}
|
Unsere showPausedNotification()-Hilfsmethode sieht ähnlich aus wie die showPlayNotification()-Methode.
1 |
private void showPausedNotification() { |
2 |
NotificationCompat.Builder builder = MediaStyleHelper.from(this, mMediaSessionCompat); |
3 |
if( builder == null ) { |
4 |
return; |
5 |
}
|
6 |
|
7 |
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_play, "Play", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE))); |
8 |
builder.setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0).setMediaSession(mMediaSessionCompat.getSessionToken())); |
9 |
builder.setSmallIcon(R.mipmap.ic_launcher); |
10 |
NotificationManagerCompat.from(this).notify(1, builder.build()); |
11 |
}
|
Die nächste Methode im Callback, die wir besprechen werden, onPlayFromMediaId(), verwendet einen String und ein Bundle als Parameter. Dies ist die Rückrufmethode, die Sie zum Ändern von Audiospuren/Inhalten in Ihrer App verwenden können.
Für dieses Tutorial akzeptieren wir einfach eine Rohressourcen-ID und versuchen, diese abzuspielen, und initialisieren dann die Metadaten der Sitzung neu. Da Sie ein Bundle an diese Methode übergeben dürfen, können Sie damit andere Aspekte Ihrer Medienwiedergabe anpassen, z. B. das Einrichten eines benutzerdefinierten Hintergrundsounds für einen Track.
1 |
@Override
|
2 |
public void onPlayFromMediaId(String mediaId, Bundle extras) { |
3 |
super.onPlayFromMediaId(mediaId, extras); |
4 |
|
5 |
try { |
6 |
AssetFileDescriptor afd = getResources().openRawResourceFd(Integer.valueOf(mediaId)); |
7 |
if( afd == null ) { |
8 |
return; |
9 |
}
|
10 |
|
11 |
try { |
12 |
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); |
13 |
|
14 |
} catch( IllegalStateException e ) { |
15 |
mMediaPlayer.release(); |
16 |
initMediaPlayer(); |
17 |
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); |
18 |
}
|
19 |
|
20 |
afd.close(); |
21 |
initMediaSessionMetadata(); |
22 |
|
23 |
} catch (IOException e) { |
24 |
return; |
25 |
}
|
26 |
|
27 |
try { |
28 |
mMediaPlayer.prepare(); |
29 |
} catch (IOException e) {} |
30 |
|
31 |
//Work with extras here if you want
|
32 |
}
|
Nachdem wir nun die beiden Hauptmethoden in diesem Rückruf besprochen haben, die Sie in Ihren Apps verwenden werden, ist es wichtig zu wissen, dass es andere optionale Methoden gibt, mit denen Sie Ihren Dienst anpassen können. Einige Methoden umfassen onSeekTo(), mit dem Sie die Wiedergabeposition Ihres Inhalts ändern können, und onCommand(), das einen String akzeptiert, der den Befehlstyp angibt, ein Bundle für zusätzliche Informationen zum Befehl und einen ResultReceiver-Rückruf, der ermöglicht es Ihnen, benutzerdefinierte Befehle an Ihren Service zu senden.
1 |
@Override
|
2 |
public void onCommand(String command, Bundle extras, ResultReceiver cb) { |
3 |
super.onCommand(command, extras, cb); |
4 |
if( COMMAND_EXAMPLE.equalsIgnoreCase(command) ) { |
5 |
//Custom command here
|
6 |
}
|
7 |
}
|
8 |
|
9 |
@Override
|
10 |
public void onSeekTo(long pos) { |
11 |
super.onSeekTo(pos); |
12 |
}
|
Abreißen
Wenn unsere Audiodatei fertig ist, möchten wir entscheiden, was unsere nächste Aktion sein wird. Während Sie vielleicht den nächsten Titel in Ihrer App abspielen möchten, halten wir die Dinge einfach und veröffentlichen den MediaPlayer.
1 |
@Override
|
2 |
public void onCompletion(MediaPlayer mediaPlayer) { |
3 |
if( mMediaPlayer != null ) { |
4 |
mMediaPlayer.release(); |
5 |
}
|
6 |
}
|
Schließlich möchten wir noch ein paar Dinge in der onDestroy()-Methode unseres Service tun. Rufen Sie zunächst einen Verweis auf den AudioManager des Systemdienstes ab und rufen Sie AbandonAudioFocus() mit unserem AudioFocusChangeListener als Parameter auf, der andere Apps auf dem Gerät benachrichtigt, dass Sie den Audiofokus aufgeben. Heben Sie als Nächstes die Registrierung des BroadcastReceivers auf, der für die Überwachung von Kopfhöreränderungen eingerichtet wurde, und geben Sie das MediaSessionCompat-Objekt frei. Schließlich möchten Sie die Benachrichtigung zur Wiedergabesteuerung abbrechen.
1 |
@Override
|
2 |
public void onDestroy() { |
3 |
super.onDestroy(); |
4 |
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
5 |
audioManager.abandonAudioFocus(this); |
6 |
unregisterReceiver(mNoisyReceiver); |
7 |
mMediaSessionCompat.release(); |
8 |
NotificationManagerCompat.from(this).cancel(1); |
9 |
}
|
An dieser Stelle sollten Sie über einen funktionierenden grundlegenden Hintergrundaudio Service verfügen, der MediaSessionCompat für die Wiedergabesteuerung auf allen Geräten verwendet. Während es bereits viel getan hat, um den Dienst zu erstellen, sollten Sie in der Lage sein, die Wiedergabe von Ihrer App, einer Benachrichtigung, einer Sperrbildschirmsteuerung auf Pre-Lollipop-Geräten (Lollipop und höher verwenden die Benachrichtigung auf dem Sperrbildschirm) zu steuern. und von Peripheriegeräten wie Android Wear, sobald der Service gestartet wurde.



Starten und Steuern von Inhalten aus einer Aktivität
Während die meisten Steuerelemente automatisch erfolgen, haben Sie noch ein wenig Arbeit, um eine Mediensitzung über Ihre In-App-Steuerelemente zu starten und zu steuern. Zumindest möchten Sie, dass in Ihrer App ein MediaBrowserCompat.ConnectionCallback-, MediaControllerCompat.Callback-, MediaBrowserCompat- und MediaControllerCompat-Objekt erstellt wird.
MediaControllerCompat.Callback verfügt über eine Methode namens onPlaybackStateChanged(), die Änderungen im Wiedergabestatus empfängt und verwendet werden kann, um Ihre Benutzeroberfläche synchron zu halten.
1 |
private MediaControllerCompat.Callback mMediaControllerCompatCallback = new MediaControllerCompat.Callback() { |
2 |
|
3 |
@Override
|
4 |
public void onPlaybackStateChanged(PlaybackStateCompat state) { |
5 |
super.onPlaybackStateChanged(state); |
6 |
if( state == null ) { |
7 |
return; |
8 |
}
|
9 |
|
10 |
switch( state.getState() ) { |
11 |
case PlaybackStateCompat.STATE_PLAYING: { |
12 |
mCurrentState = STATE_PLAYING; |
13 |
break; |
14 |
}
|
15 |
case PlaybackStateCompat.STATE_PAUSED: { |
16 |
mCurrentState = STATE_PAUSED; |
17 |
break; |
18 |
}
|
19 |
}
|
20 |
}
|
21 |
};
|
MediaBrowserCompat.ConnectionCallback verfügt über eine onConnected()-Methode, die aufgerufen wird, wenn ein neues MediaBrowserCompat-Objekt erstellt und verbunden wird. Sie können dies verwenden, um Ihr MediaControllerCompat-Objekt zu initialisieren, es mit Ihrem MediaControllerCompat.Callback zu verknüpfen und es mit MediaSessionCompat von Ihrem Service zu verknüpfen. Sobald dies abgeschlossen ist, können Sie die Audiowiedergabe mit dieser Methode starten.
1 |
private MediaBrowserCompat.ConnectionCallback mMediaBrowserCompatConnectionCallback = new MediaBrowserCompat.ConnectionCallback() { |
2 |
|
3 |
@Override
|
4 |
public void onConnected() { |
5 |
super.onConnected(); |
6 |
try { |
7 |
mMediaControllerCompat = new MediaControllerCompat(MainActivity.this, mMediaBrowserCompat.getSessionToken()); |
8 |
mMediaControllerCompat.registerCallback(mMediaControllerCompatCallback); |
9 |
setSupportMediaController(mMediaControllerCompat); |
10 |
getSupportMediaController().getTransportControls().playFromMediaId(String.valueOf(R.raw.warner_tautz_off_broadway), null); |
11 |
|
12 |
} catch( RemoteException e ) { |
13 |
|
14 |
}
|
15 |
}
|
16 |
};
|
Sie werden feststellen, dass der obige Codeausschnitt getSupportMediaController().getTransportControls() verwendet, um mit der Mediensitzung zu kommunizieren. Mit derselben Technik können Sie onPlay() und onPause() im MediaSessionCompat.Callback-Objekt Ihres Audiodienstes aufrufen.
1 |
if( mCurrentState == STATE_PAUSED ) { |
2 |
getSupportMediaController().getTransportControls().play(); |
3 |
mCurrentState = STATE_PLAYING; |
4 |
} else { |
5 |
if( getSupportMediaController().getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING ) { |
6 |
getSupportMediaController().getTransportControls().pause(); |
7 |
}
|
8 |
|
9 |
mCurrentState = STATE_PAUSED; |
10 |
}
|
Wenn Sie mit der Audiowiedergabe fertig sind, können Sie den Audiodienst anhalten und Ihr MediaBrowserCompat-Objekt trennen, was wir in diesem Tutorial tun werden, wenn diese Activity zerstört wird.
1 |
@Override
|
2 |
protected void onDestroy() { |
3 |
super.onDestroy(); |
4 |
if( getSupportMediaController().getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING ) { |
5 |
getSupportMediaController().getTransportControls().pause(); |
6 |
}
|
7 |
|
8 |
mMediaBrowserCompat.disconnect(); |
9 |
}
|
Einpacken
Wütend! Wie Sie sehen können, gibt es viele bewegliche Teile, die mit der korrekten Erstellung und Verwendung eines Hintergrundaudiodienstes verbunden sind.
In diesem Tutorial haben Sie einen Dienst erstellt, der eine einfache Audiodatei abspielt, auf Änderungen des Audiofokus wartet und eine Verknüpfung zu MediaSessionCompat erstellt, um eine universelle Wiedergabesteuerung auf Android-Geräten, einschließlich Mobilteilen und Android Wear, bereitzustellen. Wenn Sie beim Durcharbeiten dieses Tutorials auf Hindernisse stoßen, empfehle ich dringend, den zugehörigen Android-Projektcode auf dem GitHub von Envato Tuts+ zu überprüfen.
Schauen Sie sich auch einige unserer anderen Android-Kurse und -Tutorials hier auf Envato Tuts+ an!









