Reproduzir mídia em segundo plano

É possível reproduzir mídia em segundo plano mesmo quando o aplicativo não está na tela, por exemplo, enquanto o usuário interage com outros aplicativos.

Para fazer isso, incorpore a MediaPlayer em um MediaBrowserServiceCompat serviço e faça com que ela interaja com um MediaBrowserCompat em outra atividade.

Tenha cuidado ao implementar essa configuração de cliente e servidor. Há expectativas sobre como um player em execução em um serviço em segundo plano interage com o restante do sistema. Se seu aplicativo não atender a essas expectativas, o usuário poderá ter uma experiência negativa. Consulte Visão geral para um aplicativo de áudio para mais detalhes.

Esta página descreve instruções especiais para gerenciar uma MediaPlayer quando ela é implementada dentro de um serviço.

Executar de maneira assíncrona

Como um Activity, todo o trabalho em um Service é feito em uma única linha de execução por padrão. Na verdade, quando você executa uma atividade e um serviço do mesmo aplicativo, eles usam a mesma linha de execução (a principal) por padrão.

Os serviços precisam processar intents de entrada rapidamente e nunca realizar cálculos demorados durante as respostas. É necessário realizar qualquer trabalho pesado ou chamadas de bloqueio de forma assíncrona: de outra linha de execução implementada por você ou por meio das diversas facilidades do framework para processamento assíncrono.

Por exemplo, ao usar MediaPlayer na linha de execução principal, faça o seguinte:

Exemplo:

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

Processar erros assíncronos

Em operações síncronas, os erros são sinalizados com uma exceção ou um código de erro. No entanto, ao usar recursos assíncronos, notifique seu aplicativo sobre erros de maneira adequada. No caso de um MediaPlayer, implemente um MediaPlayer.OnErrorListener e defina-o na instância do seu MediaPlayer:

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!
    }
}

Quando um erro ocorre, a MediaPlayer entra no estado Error (erro) e você precisa redefini-la para usá-la novamente. Para mais detalhes, consulte o diagrama de estado completo para a MediaPlayer classe.

Usar wake locks

Ao reproduzir ou fazer streaming de música em segundo plano, use wake locks para evitar que o sistema interfira na reprodução, por exemplo, fazendo com que o dispositivo entre no modo de suspensão.

Um wake lock é um sinal para o sistema de que seu aplicativo está usando recursos que devem permanecer disponíveis mesmo quando o smartphone está ocioso.

Para que a CPU continue em execução enquanto sua MediaPlayer está sendo reproduzida, chame o método setWakeMode() ao inicializar sua MediaPlayer. A MediaPlayer mantém o wake lock especificado durante a reprodução e o libera quando é pausada ou interrompida:

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

No entanto, o wake lock adquirido nesse exemplo garante apenas que a CPU permaneça ativa. Se você estiver fazendo streaming de mídia on-line e estiver usando Wi-Fi, também manterá um WifiLock, que precisará ser adquirido e liberado manualmente. Então, quando você começar a preparar o MediaPlayer com o URL remoto, crie e adquira o wake lock de Wi-Fi.

Exemplo:

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

Quando você pausar ou interromper a mídia ou quando não precisar mais da rede, libere o wake lock:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Realizar uma limpeza

Como mencionado anteriormente, um objeto MediaPlayer pode consumir uma quantidade significativa de recursos do sistema. Por isso, mantenha-o apenas durante o tempo necessário e chame release() quando terminar de usá-lo. É importante chamar esse método de limpeza explicitamente, em vez de contar com a coleta de lixo do sistema, uma vez que pode demorar até que o coletor de lixo recupere o MediaPlayer. Isso acontece porque o coletor é sensível apenas às necessidades de memória, não à falta de outros recursos relacionados à mídia. Sendo assim, caso você esteja usando um serviço, modifique sempre o método onDestroy() para liberar a MediaPlayer:

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

Procure sempre outras oportunidades para liberar a MediaPlayer, não somente durante o desligamento. Por exemplo, se você acha que não conseguirá reproduzir mídia por um longo período de tempo (por exemplo, depois de perder a seleção de áudio), libere a existente MediaPlayer e crie-a de novo mais tarde. Por outro lado, se você acha que só interromperá a reprodução por um período muito curto, mantenha a MediaPlayer para evitar a sobrecarga de repetir a criação e a preparação novamente.

Saiba mais

O Jetpack Media3 é a solução recomendada para reprodução de mídia no seu app. Saiba mais.

Essas páginas abrangem tópicos relacionados à gravação, ao armazenamento e à reprodução de áudio e vídeo: