Um Hintergrundmusik in Capacitor abzuspielen, benötigen Sie zwei native Komponenten: die audio Hintergrundmodus auf iOS, und ein mediaPlayback Fügen Sie eine Bildschirm-Steuerung über eine Medien-Sitzung hinzu und Ihre Musik-, Podcast- oder Meditation-App bleibt abspielen, wenn der Benutzer das Telefon sperren oder zwischen Apps wechselt. Diese Anleitung zeigt beide Konfigurationen in Capacitor 8. @capgo/capacitor-native-audio für native Wiedergabe und @capgo/capacitor-media-session für Systemsteuerungen.
Warum hört Audio im Hintergrund auf
Ein Capacitor-App ist eine native Shell um einen WebView herum, folgt also jeder Plattform Lifecycle-Regeln:
| iOS | Android | |
|---|---|---|
| Was passiert im Hintergrund | Die App wird innerhalb von Sekunden suspendiert | Die Activity wird pausiert, WebView-Timer werden gedrosselt, der Prozess kann beendet werden |
| Was hält das Audio am Leben | UIBackgroundModes enthält audio, Audio-Sitzungskategorie playback |
Ein Hintergrunddienst des Typs mediaPlayback mit einer Benachrichtigung |
| Stummschalter | playback Kategorie ignoriert es. ambient respektiert es |
Kein Äquivalent |
| Steuerung auf dem Bildschirm zum Sperrbildschirm | MPNowPlayingInfoCenter und MPRemoteCommandCenter |
MediaSession mit einer Medienbenachrichtigung |
| Speicherregel | Muss akustisches Inhalt spielen, den der Benutzer erwartet (Richtlinie 2.5.4) | Diensttyp muss dem tatsächlichen Gebrauch entsprechen, wie er im Google Play Console deklariert ist. |
Keine Plattform bietet Hintergrundmusik standardmäßig an, und das Beheben nur einer Seite ist der häufigste Grund für die Fehlermeldungen "funktioniert auf iPhone, hält auf Android an".
Wählen Sie Ihre Wiedergabeansatz
Es gibt zwei gute Architekturen:
- Nativwiedergabe mit
@capgo/capacitor-native-audio: Die Audio-Decoder werden von AVFoundation auf iOS und Media3 ExoPlayer auf Android verwendet. Ideal für Musikplayer, Hörbücher, Meditation-Apps und alles, wo die WebView möglicherweise gedrosselt wird. Es unterstützt lokale Dateien, Remote-URLs, HLS-Streams, Fades, Rate und Now Playing-Metadaten. - HTML
<audio>plus@capgo/capacitor-media-session: Halten Sie Ihre bestehende Web-Player und lassen Sie den Plugin die Metadaten und Steuerungen an die OS weitergeben. Auf Android läuft es auch den Vordergrunddienst während der Sitzung ab. Ideal, wenn Sie bereits einen Web-Player haben oder einen einzigen code Pfad für Web und Nativ benötigen.
@capgo/capacitor-native-audio |
<audio> + Medien-Sitzung |
|
|---|---|---|
| Wiedergabe-Engine | Nativ | WebView |
| Webunterstützung | Ja (HTML-Audio-Fallback) | Ja |
| Benutzerinformationen auf dem Bildschirm | showNotification + notificationMetadata |
setMetadata |
| Android-Vordergrunddienst | Sie starten es | Plugin startet es während des Abspielens oder Pausen |
| Remote-Control-Ereignisse | playbackState Hörer |
setActionHandler |
| Genauere, niedrigschwellige SFX | Ja | Limitiert |
Konfigurieren Sie iOS für Hintergrundaudio
Öffnen ios/App/App/Info.plist und fügen Sie den Hintergrundmodus hinzu. Sie können auch das Kontrollkästchen "Audio, AirPlay und Bild in Bild" unter Signieren und Fähigkeiten, Hintergrundmodi in Xcode anwählen, was dieselbe Schlüssel schreibt:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
Die Audio-Sitzungskategorie spielt auch eine Rolle. @capgo/capacitor-native-audio setzt sie für Sie fest: mit showNotification: true Es verwendet .playback mit der Standardmodus, der andere Apps unterbricht (Spotify wird angehalten) und Ihre App zum Abspielen von Musik macht. Mit showNotification: false Es verwendet .playback mit mixWithOthers, der für Soundeffekte geeignet ist, aber keine Steuerung auf dem Bildschirm anzeigt. Wählen Sie eine pro App, nicht pro Track.
Wenn Sie durch <audio>spielen, aktiviert WebKit die Wiedergabe-Sitzung, wenn die Medien beginnen. Stellen Sie sicher, dass die Wiedergabe von einem Benutzerinteraktionsereignis aus zum ersten Mal gestartet wird, oder iOS blockiert es.
Mit Xcode 26 oder höher bauen, bevor Sie es in App Store Connect hochladen, was seit April 2026 erforderlich ist. Capgo Erstellen Wenn Sie auf Windows oder Linux arbeiten, können Sie das im Cloud tun.
Konfigurieren Sie Android für Hintergrundaudio
Fügen Sie die Berechtigungen hinzu android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
FOREGROUND_SERVICE_MEDIA_PLAYBACK ist für Apps, die sich auf Android 14 (API 34) oder höher richten, und Capacitor 8 API 36, erforderlich. Ohne es wird der Dienststart einen SecurityExceptionwürfeln. Wenn Sie veröffentlichen, fragt die Play Console Sie nach, die Vordergrunddiensttyp zu deklarieren und zu rechtfertigen. "Media playback, das der Benutzer gestartet hat", ist der akzeptierte Gebrauch.
POST_NOTIFICATIONS ist eine Laufzeitberechtigung auf Android 13+. Medien-Sitzungsbenachrichtigungen sind von der Benachrichtigungs-Berechtigung befreit und zeigen sich immer noch im Schatten, aber jede andere Benachrichtigung, die Ihr Dienst postet, benötigt sie.
Android 12+ blockiert den Start eines Vordergrunddienstes, während die App im Hintergrund ist. Starten Sie ihn, wenn der Benutzer auf "Play" klickt, während Ihre Aktivität sichtbar ist.
Option A: Lassen Sie den Mediensitzungs-Plugin den Dienst laufen
@capgo/capacitor-media-session erklärt einen mediaPlayback Dienst in seinem eigenen Manifest und startet ihn, wenn Sie den Abspielzustand auf setzen playing oder paused. Es stoppt, wenn der Zustand zurückgeht zu none und die App ist im Hintergrund. Wenn Sie möchten, dass der Dienst für die gesamte App-Sitzung läuft, setzen Sie die Plugin-Konfiguration in capacitor.config.ts:
plugins: {
MediaSession: {
foregroundService: 'always',
},
},
The default, starting only during playback, is what most apps want.
Option B: Starten Sie Ihren eigenen Vordergrunddienst
@capgo/capacitor-native-audio erledigt die Wiedergabe, den Fokus und die Medienbenachrichtigung, aber es erstellt keinen Vordergrunddienst. Wenn Sie es ohne den Medien-Sitzungs-Plugin verwenden, fügen Sie ein kleines Service und einen lokalen Plugin zu Ihrem Android-Projekt hinzu.
android/app/src/main/java/com/example/app/PlaybackService.java:
package com.example.app;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;
public class PlaybackService extends Service {
private static final String CHANNEL_ID = "playback";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID, "Playback", NotificationManager.IMPORTANCE_LOW);
getSystemService(NotificationManager.class).createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Playing audio")
.setSmallIcon(R.mipmap.ic_launcher)
.setOngoing(true)
.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
} else {
startForeground(1, notification);
}
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
PlaybackServicePlugin.java im gleichen Paket:
package com.example.app;
import android.content.Intent;
import androidx.core.content.ContextCompat;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
@CapacitorPlugin(name = "PlaybackService")
public class PlaybackServicePlugin extends Plugin {
@PluginMethod
public void start(PluginCall call) {
ContextCompat.startForegroundService(getContext(), new Intent(getContext(), PlaybackService.class));
call.resolve();
}
@PluginMethod
public void stop(PluginCall call) {
getContext().stopService(new Intent(getContext(), PlaybackService.class));
call.resolve();
}
}
Registrieren Sie es in MainActivity.java vorher super.onCreate, und deklarieren Sie den Dienst im Manifest:
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(PlaybackServicePlugin.class);
super.onCreate(savedInstanceState);
}
}
<service
android:name=".PlaybackService"
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
Von TypeScript:
import { Capacitor, registerPlugin } from '@capacitor/core';
interface PlaybackServicePlugin {
start(): Promise<void>;
stop(): Promise<void>;
}
export const PlaybackService = registerPlugin<PlaybackServicePlugin>('PlaybackService');
export const isAndroid = Capacitor.getPlatform() === 'android';
Play audio natively mit @capgo/capacitor-native-audio
bun add @capgo/capacitor-native-audio
bunx cap sync
Konfigurieren Sie einmalig bei Start, dann laden und abspielen:
import { NativeAudio } from '@capgo/capacitor-native-audio';
import { PlaybackService, isAndroid } from './playback-service';
await NativeAudio.configure({
focus: true, // request audio focus, pause others
background: true, // keep playing in the background
backgroundPlayback: true, // Android: skip auto pause on background
showNotification: true, // lock screen and notification controls
});
export async function playEpisode(id: string, url: string, title: string, artwork: string) {
await NativeAudio.preload({
assetId: id,
assetPath: url,
isUrl: true,
notificationMetadata: {
title,
artist: 'My Podcast',
artworkUrl: artwork,
},
});
if (isAndroid) await PlaybackService.start();
await NativeAudio.play({ assetId: id });
}
export async function stopEpisode(id: string) {
await NativeAudio.stop({ assetId: id });
await NativeAudio.unload({ assetId: id });
if (isAndroid) await PlaybackService.stop();
}
assetPath nimmt einen Pfad relativ zu Ihren Web-Ressourcen (audio/intro.mp3) an, einen file:// URL, einen https:// URL oder einen HLS- .m3u8 Stream. Für alles außer einem bundelten Asset setzen Sie isUrl: true. If your audio host requires auth, pass headers.
Bauen Sie eine Playlist, die von selbst fortschreitet:
Der complete ereignis wird ausgelöst, wenn ein Track endet, auch mit dem Bildschirm gesperrt, weil der native Player weiterläuft:
const queue = [
{ id: 'ep1', url: 'https://cdn.example.com/ep1.mp3', title: 'Episode 1' },
{ id: 'ep2', url: 'https://cdn.example.com/ep2.mp3', title: 'Episode 2' },
];
let index = 0;
await NativeAudio.addListener('complete', async ({ assetId }) => {
await NativeAudio.unload({ assetId });
index += 1;
if (index < queue.length) {
const next = queue[index];
await NativeAudio.preload({
assetId: next.id,
assetPath: next.url,
isUrl: true,
notificationMetadata: { title: next.title, artist: 'My Podcast' },
});
await NativeAudio.play({ assetId: next.id });
} else if (isAndroid) {
await PlaybackService.stop();
}
});
Halten Sie den Vordergrund-Dienst zwischen den Tracks laufend. Wenn Sie ihn nach einem Track beenden und versuchen, ihn wieder zu starten, während er im Hintergrund läuft, werfen Android 12+ ForegroundServiceStartNotAllowedException.
Halten Sie Ihre UI im Einklang mit dem Sperrbildschirm
Benutzer drücken die Pause-Taste auf dem Bildschirm, auf Kopfhörern oder in der Benachrichtigung. Hören Sie zu playbackState und behandeln Sie den nativen Player als Quelle der Wahrheit:
await NativeAudio.addListener('playbackState', (event) => {
// event.state: 'playing' | 'paused' | 'stopped'
// event.reason: 'play', 'pause', 'remotePlay', 'complete', ...
store.setPlaying(event.isPlaying);
if (event.currentTime !== undefined) store.setPosition(event.currentTime);
});
await NativeAudio.addListener('currentTime', ({ currentTime }) => {
store.setPosition(currentTime); // every 100 ms while playing
});
Auch aktualisieren Sie den Zustand, wenn die App wieder in den Vordergrund kommt, mit isPlaying und getCurrentTimeda der WebView möglicherweise während der Ereignisemissionen angehalten wurde.
Verwenden Sie ein HTML-Audio-Element mit Medien-Sitzungssteuerungen
If you already have a web player, keep it and add the plugin:
bun add @capgo/capacitor-media-session
bunx cap sync
import { MediaSession } from '@capgo/capacitor-media-session';
const audio = new Audio();
audio.preload = 'auto';
export async function play(track: { url: string; title: string; artist: string; cover: string }) {
audio.src = track.url;
await audio.play();
await MediaSession.setMetadata({
title: track.title,
artist: track.artist,
artwork: [{ src: track.cover, sizes: '512x512', type: 'image/jpeg' }],
});
await MediaSession.setPlaybackState({ playbackState: 'playing' });
}
await MediaSession.setActionHandler({ action: 'play' }, async () => {
await audio.play();
await MediaSession.setPlaybackState({ playbackState: 'playing' });
});
await MediaSession.setActionHandler({ action: 'pause' }, async () => {
audio.pause();
await MediaSession.setPlaybackState({ playbackState: 'paused' });
});
await MediaSession.setActionHandler({ action: 'seekto' }, (details) => {
if (details.seekTime != null) audio.currentTime = details.seekTime;
});
await MediaSession.setActionHandler({ action: 'nexttrack' }, () => playNext());
audio.addEventListener('timeupdate', () => {
MediaSession.setPositionState({
duration: audio.duration || 0,
position: audio.currentTime,
playbackRate: audio.playbackRate,
});
});
Aufrufen Sie setPositionState Page/area: Capgo Marketing Website. Rolle: Kurze UI-Bezeichnung oder Navigationselement. Gesehen in: Seite trust.astro. Nachrichtsschlüssel `and` (Und). timeupdate Ereignisse verbrauchen die Batterie.
ereignet sich auf Kosten der Akkulaufzeit.
Phone calls, Siri, alarms and other apps interrupt playback. Native audio pauses on interruption and, on iOS, resumes when the system says it should. Two more cases need your attention:
- Kopfhörer abgezogen: Benutzer erwarten, dass die Wiedergabe anhält. Auf Android registrieren Sie einen Empfänger für
AudioManager.ACTION_AUDIO_BECOMING_NOISYim Dienst, wenn Ihr Spieler die Wiedergabe nicht bereits anhält. Auf iOS hören Sie nach Routenänderungen mit@capgo/capacitor-audio-session:
import { AudioSession, RouteChangeReasons } from '@capgo/capacitor-audio-session';
await AudioSession.addListener('routeChanged', (reason) => {
if (reason === RouteChangeReasons.OLD_DEVICE_UNAVAILABLE) {
pausePlayback();
}
});
await AudioSession.addListener('interruption', (type) => {
// 'began' or 'ended'
});
- Ducking vs. Anhalten: Navigationsempfehlungen drücken Ihre Audio ab. Apps mit gesprochenem Wort sollten anhalten, da gedämpfte Sprache schwer zu folgen ist.
Testen Sie Hintergrundwiedergabe
Simulatoren liegen über Hintergrundverhalten falsch. Testen Sie auf echten Geräten:
- Starte die Wiedergabe, sperre das Gerät, warte zwei Minuten. Der Audio muss weiterlaufen.
- Use the lock screen play, pause, next and seek controls, then unlock and check your UI matches.
- Play, switch to another app that plays audio, then come back.
- Starten Sie einen Anruf oder FaceTime-Audio während der Wiedergabe.
- Auf Android aktivieren Sie die Energieeinsparung und testen Sie auf einem Samsung- oder Xiaomi-Gerät. Einige OEMs töten Hintergrundanwendungen aggressiv. Ein Vordergrunddienst überlebt, eine einfache Hintergrundaufgabe nicht.
- Auf Android 13+, verweigern Sie die Benachrichtigungsrechte und bestätigen Sie, dass das Abspielen weiterhin funktioniert.
- Ein Wiedergabeliste läuft durch mehrere Tracks mit dem Bildschirm ausgeschaltet.
adb shell dumpsys activity services | grep -A3 PlaybackService zeigt an, ob Ihr Dienst im Vordergrund läuft.
Troubleshooting
Audio hört nach etwa 30 Sekunden auf, wenn man iOS verwendet.: UIBackgroundModes mangelt audiooder die Sitzungskategorie ist ambient oder soloAmbient. Check Info.plist im der gebauten App, nicht nur in Ihrem Quellcode.
Der Audio spielt nur wenige Minuten auf Android.: kein Vordergrunddienst oder der Dienst ist nicht vom Typ mediaPlayback. Überprüfen Sie auch die OEM-Einstellungen für die Akkubatterie.
SecurityException: Starting FGS with type mediaPlayback ... requires permissions: hinzufügen FOREGROUND_SERVICE_MEDIA_PLAYBACK.
ForegroundServiceStartNotAllowedException: Sie haben den Dienst während der App im Hintergrund gestartet. Starten Sie ihn beim Anstoss des Benutzers.
Keine Steuerung auf dem iOS-Schutzschirm: showNotification ist falsch (mixbare Sitzung), oder wurde kein Metadaten-Set gesetzt. Nur die App, die eine nicht-mixbare Sitzung besitzt, wird zum Now Playing-App.
Zwei Benachrichtigungen auf Android: Sie haben showNotification in native Audio und auch Ihre eigene Dienstbenachrichtigung gepostet. Halten Sie die Dienstbenachrichtigung minimal, oder verwenden Sie nur den Dienst des Media-Session-Plugins.
Andere Musik-Apps spielen weiterhin über Ihre: Sie haben eine mixbare Sitzung konfiguriert. Setzen Sie focus: true und showNotification: true.
Schiffen Sie Änderungen am Player schneller
Die Logik, die Benutzeroberfläche, die Metadaten und die Analysen Ihres Spielers leben in JavaScript. Sobald die native Konfiguration oben in einem Store-Build ist, Capgo Live-Updates lassen Sie Sie Fixes an das code pushen, ohne ein neues Review-Zyklus zu benötigen. Die native Audio-Dokumentation und Die Medien-Sitzungs-Dokumentation umfasst alle Optionen, und wenn Ihr App auch Audio aufzeichnet, sehen Sie sich die Capacitor Audio-Recorder-Plugin.