É 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:
- Chame
prepareAsync()em vez deprepare()e - Implemente um
MediaPlayer.OnPreparedListenerpara receber uma notificação quando a preparação for concluída e você puder começar a reprodução.
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: