Audio Streaming in Background | Things must to do

Gaurav Bansal
3 min readJan 19, 2020

While playing audio/video content, the basic steps to setup player with streaming urls and handling their callbacks are generally done by every developer but the use cases which can arrive while you support background audio play, can be missed. So to do cross check of your implementation, give a read to this article.

  1. Always acquire Audio focus first, before play.

2. Acquire Partial Wake lock and Full Wifi Lock.

3. Make your streaming service to always run in foreground

1) Acquiring Audio Focus :

a) Define Audio Focus Listener as below

private final AudioManager.OnAudioFocusChangeListener _audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback in case of transient loss

break;

case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and
// release media player

break;

case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop playback.
// We don't release the media player because playback
// is likely to resume

break;

case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time e.g device notification, but it's ok to keep playing at
// an attenuated level, should change volume of the player here

break;

case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
// resume playback after short playback stop

break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
break;
}
}
};
b) Before play, request for audio focus as below code, if it returns true that means now we are authorised to execute our play media code.private boolean grabAudioFocus() {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
int result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result = audioManager.requestAudioFocus(
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(
new AudioAttributes.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build())
.setOnAudioFocusChangeListener(_audioFocusChangeListener)
.build());
} else {
result = audioManager.requestAudioFocus(_audioFocusChangeListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}

if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
// Show Error Toast on Player Screen (if it exists)

return false;
}

// grab the media button when we have audio focus
return true;
}
c) When music pauses or stops, need to release audio focus.public void releaseAudioFocus() {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.abandonAudioFocus(_audioFocusChangeListener);
}

2. Acquire Partial Wake Lock and Full Wifi Lock

Since Android 6.0 Google introduced Doze mode and App sleep mode to save battery of the user, under which CPU sleeps in background after a certain duration and Wifi is also released for that duration.It can cause our playback to be stopped due to no-internet. To avoid this we need to acquire CPU lock and Wifi Lock.

A) Wifi Lock:

i) Define Wifi Lock in onCreate() of the service

_myWifiLock = ((WifiManager) getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

ii) acquire Wifi Lock before starting any playback.

public void acquireWifiLock() {
// Acquire WiFi lock to ensure that the music playback service
// continues to run without system interruption
try {
if (_myWifiLock.isHeld()) {
return;
}

_myWifiLock.acquire();
} catch (Exception ex) {
// Nevermind, exception only comes if wifilock was already
// released elsewhere
ex.printStackTrace();
}
}

iii) release wifi lock when music pauses or stops.

private void releaseWifiLock() {
try {
if (_myWifiLock.isHeld()) {
_myWifiLock.release();
}
} catch (Exception ex) {
// Nevermind, exception only comes if wifilock was already released
// elsewhere
ex.printStackTrace();
}
}

B) Wake Lock

i) Set wake mode before any playback starts.

public void setWakeMode() {
PowerManager pm = (PowerManager) _myAppContext.getSystemService(Context.POWER_SERVICE);
if(wakeLock == null) {
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.toString());
wakeLock.setReferenceCounted(false);
}

if(!wakeLock.isHeld())
wakeLock.acquire();
}

ii) release Wake mode while music pauses or stops.

public void releaseWakeMode() {
if(wakeLock != null && wakeLock.isHeld()) {
wakeLock.release();
wakeLock = null;
}
}

3. Always make service as foreground service :

Even after handling above cases, app doze mode still can ask CPU to sleep and ignores all the locks which we acquired, it only respect the constraints if the streaming service is in foreground. so this step is also very important to implement.

To make any service a foreground service, we should handle notification creation and then ask to run service in foreground.

builder = new NotificationCompat.Builder(mContext, NotificationChannelHelper.PLAY_CHANNEL_ID)
.setStyle(mediaStyle)
.setSmallIcon(icon)
.setShowWhen(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(getPendingIntent());
Notification mNotification = builder.build();
((MusicService) mContext).startForeground(Constants.NOTIFICATION_ID_NOW_PLAYING, mNotification);

Since Android O, the above method to start service in foreground, should be called within 5 seconds of invoking service, If you would not able to do so, it will throw the exception.

Let me know if you face any issues or you have any suggestions.

p.s. In Android 6.0 there is an OS issue, in which, even though you do follow above steps, then also your streaming stops in background in some cases. If you face this, let me know in the comments, I have a hack solution to handle that too ;-) Happy Streaming ~~

--

--

Gaurav Bansal

AVP Technical Architect in Sony Pictures, Ex Gaana. Interested to share what I learned and what I experienced and enthusiastic to learn more new things everyday