É possível reproduzir mídia em segundo plano mesmo quando o app não está na tela, por exemplo, enquanto o usuário interage com outros apps.
Para fazer isso, incorpore o MediaPlayer em um serviço MediaBrowserServiceCompat
e faça com que ele 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 o aplicativo não atender a essas expectativas, o usuário poderá ter uma experiência ruim. Consulte Criar um app de áudio para saber mais.
Esta página descreve instruções especiais para gerenciar uma MediaPlayer quando ela é implementada dentro de um serviço.
Executar de forma assíncrona
Como uma Activity
, todo o trabalho em uma 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 "linha de execução principal") por padrão.
Os serviços precisam processar intents de entrada rapidamente e nunca realizar cálculos demorados ao responder a elas. É necessário realizar qualquer trabalho pesado ou chamadas de bloqueio de forma assíncrona: de outra linha de execução implementada por você ou usando as diversas ferramentas do framework para processamento assíncrono.
Por exemplo, ao usar MediaPlayer
na linha de execução principal, faça o seguinte:
- Chame
prepareAsync()
em vez deprepare()
e - Implemente um
MediaPlayer.OnPreparedListener
para receber uma notificação quando a preparação for concluída e você puder começar a jogar.
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 o
aplicativo sobre erros de maneira adequada. No caso de um MediaPlayer
, implemente um MediaPlayer.OnErrorListener
e defina-o na instância
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
muda para o estado Error. É necessário
redefinir o objeto antes de usá-lo novamente. Para mais detalhes, consulte o diagrama de estado completo
da classe MediaPlayer
.
Usar wake locks
Ao tocar ou fazer streaming de música em segundo plano, use bloqueios de ativação para impedir que o sistema interfira na reprodução, por exemplo, fazendo o dispositivo entrar no modo de suspensão.
Um wake lock é um indicador para o sistema de que seu aplicativo está usando recursos que precisam permanecer disponíveis mesmo quando o smartphone está ocioso.
Para garantir que a CPU continue em execução enquanto o MediaPlayer
é
reproduzido, chame o método setWakeMode()
ao inicializar o
MediaPlayer
. A MediaPlayer
mantém o bloqueio 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 mantenha um WifiLock
, que precisa ser
adquirido e liberado manualmente. Portanto, quando você começar a preparar a
MediaPlayer
com o URL remoto, crie e adquira o bloqueio 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 bloqueio:
Kotlin
wifiLock.release()
Java
wifiLock.release();
Limpar
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,
porque pode demorar até que o coletor de lixo recupere a
MediaPlayer
, já que ele é 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 (por exemplo, depois de perder a seleção de áudio), libere a
MediaPlayer
e crie-a de novo mais tarde. Por outro lado, se você
espera interromper a reprodução por um período muito curto, é melhor manter
a MediaPlayer
para evitar a sobrecarga de criar e preparar
de novo.
Saiba mais
O Jetpack Media3 é a solução recomendada para a reprodução de mídia no seu app. Saiba mais.
Estas páginas abrangem tópicos relacionados à gravação, ao armazenamento e à reprodução de áudio e vídeo: