Saltar al contenido principal
Tutorial Capacitor Móvil

Cómo reproducir audio en segundo plano en Capacitor

Reproduce audio en segundo plano en Capacitor: modo de audio de fondo en iOS, servicio de primer plano en Android, controles de pantalla de bloqueo, listas de reproducción y interrupciones.

Créditos del artículo

Martín Donadieu

Escritor

Valeria

Revisor

Jordan

Editor

Cómo reproducir audio en segundo plano en Capacitor

Para reproducir audio en segundo plano en Capacitor, necesitas dos piezas nativas: el audio modo de fondo en iOS, y un mediaPlayback servicio de primer plano en Android. Agrega controles de pantalla de bloqueo a través de una sesión de medios, y tu aplicación de música, podcast o meditación sigue reproduciendo cuando el usuario bloquea el teléfono o cambia de aplicaciones. Este manual muestra ambos conjuntos en Capacitor 8, con @capgo/capacitor-native-audio para la reproducción nativa y @capgo/capacitor-media-session para controles del sistema.

¿Por qué el audio se detiene en segundo plano?

Una aplicación Capacitor es una caja nativa alrededor de un WebView, por lo que sigue las reglas de ciclo de vida de cada plataforma.

iOS Android
¿Qué sucede en segundo plano? La aplicación se suspende en segundos La actividad se pausa, los temporizadores del WebView ralentizan, el proceso puede ser eliminado
¿Qué mantiene el audio vivo? UIBackgroundModes contiene audio, categoría de sesión de audio playback A servicio de primer plano de tipo mediaPlayback con una notificación
Botón de silencio playback ignora la categoría ambient lo respeta No tiene equivalente
Controles de pantalla de bloqueo MPNowPlayingInfoCenter y MPRemoteCommandCenter MediaSession con una notificación de estilo de medios
Regla de la tienda Debe reproducir contenido audible que el usuario espera (directriz 2.5.4) Tipo de servicio de primer plano debe coincidir con el uso real, declarado en el Console de Play

Ni plataforma ofrece reproducción de audio de fondo por defecto, y solucionar solo un lado es la razón más común para los informes de errores de "funciona en iPhone, se detiene en Android".

Elige tu enfoque de reproducción

Hay dos buenas arquitecturas:

  1. Reproducción nativa con @capgo/capacitor-native-audio: el audio se decodifica mediante AVFoundation en iOS y Media3 ExoPlayer en Android. Mejor para reproductores de música, audiolibros, aplicaciones de meditación y cualquier cosa donde la WebView podría estar limitada. Soporta archivos locales, URLs remotos, flujos HLS, transiciones, velocidad y metadatos de reproducción en curso.
  2. HTML <audio> y @capgo/capacitor-media-session: mantén tu reproductor web existente y deja que el complemento publique metadatos y controles al sistema. En Android también ejecuta el servicio de fondo mientras la sesión está reproduciendo. Mejor cuando ya tienes un reproductor web o necesitas un solo code camino para web y nativo.
@capgo/capacitor-native-audio <audio> + sesión de medios
Motor de reproducción Native context
Apoyo web Sí (fallback de audio HTML) Sí
Metadata de pantalla de bloqueo showNotification + notificationMetadata setMetadata
Servicio de primer plano de Android Comienza uno Se inicia mientras se reproduce o se pausa
Eventos de control remoto playbackState listener setActionHandler
Preciso efecto sonoro de baja latencia Sí Limitado

Configura iOS para audio de fondo

Abrir ios/App/App/Info.plist y agrega el modo de fondo. También puedes marcar "Audio, AirPlay y Picture in Picture" en Signing &amp; Capabilities, Background Modes en Xcode, que escribe la misma clave:

<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

La categoría de la sesión de audio también importa. @capgo/capacitor-native-audio lo establece para ti: con showNotification: true lo utiliza .playback Con el modo predeterminado, que interrumpe otras aplicaciones (Spotify se pausa) y convierte tu aplicación en la aplicación de reproducción en curso. showNotification: false lo utiliza .playback lo utiliza mixWithOthers, que es adecuado para efectos de sonido pero no muestra controles de pantalla de bloqueo. Elige uno por aplicación, no por pista.

Si reproducir a través de <audio>WebKit activa la sesión de reproducción cuando comienza el medio. Asegúrate de que la reproducción se inicie desde un gesto del usuario la primera vez, o iOS bloqueará la reproducción.

Construya con Xcode 26 o posterior antes de subir a App Store Connect, lo cual ha sido requerido desde abril de 2026. Capgo Construir puedes hacer eso en la nube si trabajas en Windows o Linux.

Configurear Android para audio de fondo

Configure Android para audio de fondo 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 Es obligatorio para aplicaciones que se dirijan a Android 14 (API 34) o superior, y Capacitor 8 apunta a API 36. Sin él, iniciar el servicio lanza un error SecurityExceptiones obligatorio para aplicaciones que se dirigen a Android 14 (__CAPGO_KEEP_0__ 34) o superior, y __CAPGO_KEEP_1__ 8 se dirige a __CAPGO_KEEP_2__ 36. Sin él, iniciar el servicio lanza un

POST_NOTIFICATIONS . Cuando publique, Play Console le pide que declare el tipo de servicio de primer plano y lo justifique. 'Reproducción de medios iniciada por el usuario' es el uso aceptado.

Android 12+ blocks starting a foreground service while the app is in the background. Start it when the user presses play, while your activity is visible.

Opción A: dejar que el plugin de sesión de medios ejecute el servicio

@capgo/capacitor-media-session declara un mediaPlayback declara un servicio en su propio manifiesto y lo inicia cuando establezca el estado de reproducción a playing o pausedNo lo detiene cuando el estado vuelve a none y la aplicación está en segundo plano. Si deseas que el servicio se ejecute durante toda la sesión de la aplicación, configura la configuración del plugin en capacitor.config.ts:

plugins: {
  MediaSession: {
    foregroundService: 'always',
  },
},

The default, starting only during playback, is what most apps want.

Opción B: iniciar su propio servicio de foreground

@capgo/capacitor-native-audio handles playback, focus and the media notification, but it does not create the foreground service. If you use it without the media session plugin, add a small service and a local plugin to your Android project.

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 en el mismo paquete:

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

Regístralo MainActivity.java o super.onCreatey declara el servicio en el manifiesto:

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" />

Desde 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';

Reproduce audio nativamente con @capgo/capacitor-native-audio

bun add @capgo/capacitor-native-audio
bunx cap sync

Configura una vez al inicio, luego carga y reproduce:

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 acepta un camino relativo a tus activos web (audio/intro.mp3) , un file:// una https:// una URL o un flujo HLS .m3u8 Para cualquier cosa que no sea un activo embutido, establece isUrl: trueSi tu host de audio requiere autenticación, pasa headers.

Construye una lista de reproducción que avance por su cuenta

El complete evento se dispara cuando un track termina, incluso con la pantalla bloqueada, porque el jugador nativo sigue funcionando:

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

Mantén el servicio de primer plano en ejecución entre tracks. Si lo detienes después de un track y tratas de iniciar de nuevo mientras estás en segundo plano, Android 12+ lanza ForegroundServiceStartNotAllowedException.

Keep your UI in sync with the lock screen

Los usuarios presionarán pausa en la pantalla de bloqueo, en los auriculares o en la notificación. Escuchen a playbackState y traten al jugador nativo como la fuente de verdad:

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

Refresque el estado también cuando la aplicación regrese al primer plano, utilizando isPlaying y getCurrentTimeya que la WebView puede haber sido pausada mientras se emitían eventos.

Utilice un elemento de audio HTML con controles de sesión de medios

Si ya tiene un reproductor web, manténgalo y agregue el 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,
  });
});

Llame setPositionState al menos una vez por segundo en producción. La pantalla de bloqueo interpola entre actualizaciones, y el puente se llena con timeupdate gestos desperdicia la batería.

Gestione las interrupciones y los cambios de ruta

Las llamadas telefónicas, Siri, las alarmas y otras aplicaciones interrumpen la reproducción. El audio nativo pausa la reproducción en caso de interrupción y, en iOS, reanuda cuando el sistema lo indique. Dos casos más necesitan su atención:

  • Auriculares desconectados: los usuarios esperan que la reproducción se detenga. En Android, registre un receptor para AudioManager.ACTION_AUDIO_BECOMING_NOISY en su servicio si su reproductor no pausa ya. En iOS, escuche cambios de ruta con @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. pausarLos navegadores ocultan tu audio. Las aplicaciones de palabras habladas deberían pausar en su lugar, ya que la voz baja es difícil de seguir.

Prueba la reproducción de fondo

Los simuladores mienten sobre el comportamiento de fondo. Prueba en dispositivos reales:

  1. Comienza la reproducción, bloquea la pantalla, espera dos minutos. El audio debe seguir sonando.
  2. Utiliza el control de reproducción de la pantalla bloqueada, pausa, siguiente y busca, luego desbloquea y verifica que tu interfaz coincida.
  3. Reproduce, cambia a otra aplicación que reproduce audio, luego vuelve.
  4. Inicia una llamada de teléfono o FaceTime de audio durante la reproducción.
  5. En Android, habilita el ahorro de batería y prueba en un dispositivo Samsung o Xiaomi. Algunos fabricantes de dispositivos matan aplicaciones de fondo con agresividad. Un servicio de primer plano sobrevive, una tarea de fondo simple no lo hace.
  6. On Android 13+, denie la permiso de notificación y confirma que la reproducción sigue funcionando.
  7. Deje que una lista de reproducción corra a través de varios temas con la pantalla apagada.

adb shell dumpsys activity services | grep -A3 PlaybackService indica si tu servicio está ejecutándose en primer plano.

Solución de problemas

El audio se detiene después de unos 30 segundos en iOS: UIBackgroundModes falta audio, o la categoría de sesión es ambient o soloAmbient. Check Info.plist en la aplicación construida, no solo en tu código fuente.

El audio se detiene después de unos minutos en Android.:no hay servicio de primer plano, o el servicio no es de tipo mediaPlayback. Also check OEM battery optimization settings.

SecurityException: Starting FGS with type mediaPlayback ... requires permissions: agregar FOREGROUND_SERVICE_MEDIA_PLAYBACK.

ForegroundServiceStartNotAllowedException: iniciaste el servicio mientras la aplicación estaba en segundo plano. Inicia el servicio al tocar el botón de reproducción del usuario.

No controles de pantalla de bloqueo en iOS: showNotification es falso (sesión mezclable), o no se estableció ningún metadato. Solo la aplicación que posee una sesión no mezclable se convierte en la aplicación de reproducción en curso.

Dos notificaciones en Android: habilitaste showNotification en audio nativo y también publicaste tu propia notificación de servicio. Mantén la notificación de servicio minimalista, o utiliza solo el plugin de sesión de medios.

Other music apps keep playing over yours: configuraste una sesión mezclable. Establece focus: true y showNotification: true.

Envía cambios de reproductor más rápido

Queue logic, UI, metadata, and analytics for your player live in JavaScript. Once the native setup above is in a store build, Capgo actualizaciones en vivo le permiten enviar correcciones a ese code sin un nuevo ciclo de revisión. Los documentos de audio nativos y documentación de sesión de medios cubra todas las opciones, y si tu aplicación también graba audio, consulta Capacitor audio recorder plugin.

Actualizaciones en vivo para aplicaciones de Capacitor

Cuando un error de capa web está vivo, envíe la corrección a través de Capgo en lugar de esperar días para la aprobación de la tienda. Los usuarios obtienen la actualización en segundo plano mientras los cambios nativos siguen en el camino de revisión normal.

Últimas noticias de nuestro Blog

Capgo te brinda las mejores herramientas para crear una aplicación móvil profesional.