Sie können Medien im Hintergrund abspielen, auch wenn Ihre App nicht auf dem Display angezeigt wird, z. B. wenn der Nutzer mit anderen Apps interagiert.
Dazu betten Sie den MediaPlayer in einen MediaBrowserServiceCompat
-Dienst ein und lassen ihn mit einem MediaBrowserCompat
in einer anderen Aktivität interagieren.
Seien Sie bei der Implementierung dieser Client- und Serverkonfiguration vorsichtig. Es gibt Erwartungen, wie ein Player, der in einem Hintergrunddienst ausgeführt wird, mit dem Rest des Systems interagiert. Wenn Ihre Anwendung diese Erwartungen nicht erfüllt, kann die Nutzererfahrung beeinträchtigt werden. Weitere Informationen finden Sie unter Audio-Apps erstellen.
Auf dieser Seite werden spezielle Anweisungen zum Verwalten eines MediaPlayers beschrieben, wenn du ihn in einem Dienst implementierst.
Asynchron ausführen
Wie bei einem Activity
werden auch bei einem Service
standardmäßig alle Aufgaben in einem einzigen Thread ausgeführt. Wenn Sie eine Aktivität und einen Dienst aus derselben Anwendung ausführen, verwenden sie standardmäßig denselben Thread (den „Hauptthread“).
Dienste müssen eingehende Intents schnell verarbeiten und dürfen bei der Beantwortung keine langen Berechnungen ausführen. Alle zeitaufwendigen Aufgaben oder blockierenden Aufrufe müssen asynchron ausgeführt werden: entweder über einen anderen Thread, den Sie selbst implementieren, oder mithilfe der vielen Funktionen des Frameworks für die asynchrone Verarbeitung.
Wenn du beispielsweise MediaPlayer
aus deinem Hauptthread verwendest, solltest du Folgendes beachten:
- Rufen Sie
prepareAsync()
anstelle vonprepare()
auf und - Implementiere einen
MediaPlayer.OnPreparedListener
, um benachrichtigt zu werden, wenn die Vorbereitung abgeschlossen ist und du mit dem Testen beginnen kannst.
Beispiel:
Kotlin
private const val ACTION_PLAY: String = "com.example.action.PLAY"
class MyService: Service(), MediaPlayer.OnPreparedListener {
private var mMediaPlayer: MediaPlayer? = null
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
...
val action: String = intent.action
when(action) {
ACTION_PLAY -> {
mMediaPlayer = ... // initialize it here
mMediaPlayer?.apply {
setOnPreparedListener(this@MyService)
prepareAsync() // prepare async to not block main thread
}
}
}
...
}
/** Called when MediaPlayer is ready */
override fun onPrepared(mediaPlayer: MediaPlayer) {
mediaPlayer.start()
}
}
Java
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mediaPlayer = ... // initialize it here
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
Asynchrone Fehler behandeln
Bei synchronen Vorgängen werden Fehler mit einer Ausnahme oder einem Fehlercode signalisiert. Wenn Sie jedoch asynchrone Ressourcen verwenden, sollten Sie Ihre Anwendung über Fehler entsprechend informieren. Bei einer MediaPlayer
implementieren Sie einen MediaPlayer.OnErrorListener
und legen ihn in Ihrer MediaPlayer
-Instanz fest:
Kotlin
class MyService : Service(), MediaPlayer.OnErrorListener {
private var mediaPlayer: MediaPlayer? = null
fun initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer?.setOnErrorListener(this)
}
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
Java
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mediaPlayer;
public void initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer.setOnErrorListener(this);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
Wenn ein Fehler auftritt, wechselt die MediaPlayer
in den Status Error. Sie müssen sie dann zurücksetzen, bevor Sie sie wieder verwenden können. Weitere Informationen finden Sie im vollständigen Zustandsdiagramm für die Klasse MediaPlayer
.
Wakelocks verwenden
Wenn Sie Musik im Hintergrund abspielen oder streamen, müssen Sie Wakelocks verwenden, um zu verhindern, dass das System die Wiedergabe stört, z. B. durch das Einschlafen des Geräts.
Ein Wakelock ist ein Signal an das System, dass Ihre Anwendung Funktionen verwendet, die auch dann verfügbar sein sollten, wenn das Smartphone inaktiv ist.
Damit die CPU weiter ausgeführt wird, während MediaPlayer
wiedergegeben wird, rufe die Methode setWakeMode()
auf, wenn du MediaPlayer
initialisierst. Mit der Taste MediaPlayer
wird die angegebene Sperre während der Wiedergabe aufrechterhalten und bei einer Pause oder einem Stopp aufgehoben:
Kotlin
mediaPlayer = MediaPlayer().apply {
// ... other initialization here ...
setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}
Java
mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
Die in diesem Beispiel erworbene Wakelock sorgt jedoch nur dafür, dass die CPU aktiv bleibt. Wenn Sie Medien über das Netzwerk streamen und WLAN verwenden, sollten Sie auch eine WifiLock
halten, die Sie manuell erwerben und freigeben müssen. Wenn Sie also mit der Vorbereitung der MediaPlayer
mit der Remote-URL beginnen, sollten Sie die WLAN-Sperre erstellen und erwerben.
Beispiel:
Kotlin
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")
wifiLock.acquire()
Java
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
Wenn du die Medien pausierst oder beendest oder das Netzwerk nicht mehr benötigst, solltest du die Sperre aufheben:
Kotlin
wifiLock.release()
Java
wifiLock.release();
Bereinigung ausführen
Wie bereits erwähnt, kann ein MediaPlayer
-Objekt erhebliche Systemressourcen beanspruchen. Sie sollten es daher nur so lange beibehalten, wie Sie es benötigen, und release()
aufrufen, wenn Sie es nicht mehr benötigen. Es ist wichtig, diese Bereinigungsmethode explizit aufzurufen, anstatt sich auf die Systembereinigung zu verlassen. Es kann nämlich einige Zeit dauern, bis der Garbage Collector die MediaPlayer
wiederverwendet, da er nur auf den Speicherbedarf und nicht auf den Mangel an anderen medienbezogenen Ressourcen reagiert. Wenn Sie also einen Dienst verwenden, sollten Sie die Methode onDestroy()
immer überschreiben, damit MediaPlayer
freigegeben wird:
Kotlin
class MyService : Service() {
private var mediaPlayer: MediaPlayer? = null
// ...
override fun onDestroy() {
super.onDestroy()
mediaPlayer?.release()
}
}
Java
public class MyService extends Service {
MediaPlayer mediaPlayer;
// ...
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) mediaPlayer.release();
}
}
Außer beim Herunterfahren sollten Sie immer nach anderen Möglichkeiten suchen, Ihre MediaPlayer
freizugeben. Wenn du beispielsweise davon ausgehst, dass du über einen längeren Zeitraum keine Medien abspielen kannst (z. B. wenn der Audiofokus verloren geht), solltest du deine vorhandene MediaPlayer
aufheben und später noch einmal erstellen. Wenn Sie die Wiedergabe jedoch nur für eine sehr kurze Zeit unterbrechen möchten, sollten Sie die MediaPlayer
beibehalten, um den Aufwand für das Erstellen und Vorbereiten zu vermeiden.
Weitere Informationen
Jetpack Media3 ist die empfohlene Lösung für die Medienwiedergabe in Ihrer App. Weitere Informationen
Auf diesen Seiten finden Sie Themen zum Aufzeichnen, Speichern und Abspielen von Audio- und Videoinhalten: