Error with audio playback on Android


#1

We are playing a custom sound along with certain events in our app. Some of our Android users (on standalone build) are experiencing app crashes every time a sound plays. Here’s the error log:

abi24_0_0.host.exp.exponent.modules.api.av.AVModule$AudioFocusNotAcquiredException: This experience is currently in the background, so audio focus could not be acquired.

The sounds play while the app is in the foreground. Any thoughts on this?


#2

I’m not sure, we haven’t had any other reports of this issue as far as I can tell. What type of Android device and what version of Android are the affected users running?

cc @jesse and @sjchmiela.


#3

Affected users seem to be on Android version 24 for now

The two devices we know of are:
SM-G610M
Moto G (4)


#4

Hi @mikesholiu!

That’s strange… This exception is thrown only by the AVModule.acquireAudioFocus method and it’s always caught in code.

  • AVModule.acquireAudioFocus is called by
    • PlayerData.playPlayerWithRateAndMuteIfNecessary which throws and is called by
      • PlayerData.handleAudioFocusGained which catches the exception and does nothing,
      • PlayerData.onResume which catches the exception and does nothing,
      • PlayerData.applyNewStatus which throws and is called by
        • SimpleExoPlayerData.onPlayerStateChanged which catches the exception and does nothing,
        • PlayerData.setStatusWithListener which catches the exception, abandons audio focus if it’s unused and rejects the promise of AVModule.loadForSound or VideoView.setUri or AVModule.setStatus.
  1. How do you load and setStatus of the sound? Maybe you don’t catch the exception on JS side and unhandled promise rejection crashes the application?
  2. This exception is thrown based on the value of mAppIsPaused property of AVModule which is updated by onHostResume which definitely should be called when app comes to foreground. It would be strange for this code to fail.

#5

Thanks @sjchmiela,

The code is below:

const playSound = async (sound) => {
    try {

        // Load sound

        const soundObject = new (Expo.Audio.Sound)()
        await soundObject.loadAsync(sound.soundFile)

        // Before playing, create a callback that will set `shouldPlay` to `false` after it
        // finishes playing -- without this, sounds played previously will play every time the app is re-foregrounded.

        const onPlaybackStatusUpdate = async (playbackStatus) => {
            if (playbackStatus.didJustFinish) {
                await soundObject.setStatusAsync({ shouldPlay: false})
            }
        }
        soundObject.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate)

        // Play the sound

        await soundObject.setStatusAsync({
            shouldPlay: true,
            positionMillis: 0,
        })

    } catch (error) {
        if (error.code === 'E_AV_SEEKING') {
            // Do nothing; this is expected when multiple sounds are played in a row.
        } else {
            console.error(error)
        }
    }
}

#6

Thanks for the reply! I’ve got two ideas:

  1. It’s just a hunch, but doesn’t console.error cause red box to appear, which in production crashes the application? I may be wrong on this one, the only mention of this behavior I found was is here, and information there may be outdated.
  2. Silly mistake, you don’t try-catch error in onPlaybackStatusUpdate, but that shouldn’t cause this exception to be raised, as you set shouldPlay to false

I’ll try out your code later, maybe I’ll find something more. :slight_smile:


#7

@sjchmiela Ah! You are right (on iOS it doesn’t crash the app but with Android console.error() causes it to restart). Thanks for sending that link. I believe this should be the fix. Really appreciate the timely response.