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?
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?
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.
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?
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.
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)
}
}
}
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.
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.
@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.
same problem … We are playing a custom sound along with certain events in our app. It is playing when app is foreground but app running in background it is not. Here’s the error log:
[Unhandled promise rejection: Error: abi32_0_0.host.exp.exponent.modules.api.av.AVModule$AudioFocusNotAcquiredException: This experience is currently in the background, so audio focus could not be acquired.]
node_modules\react-native\Libraries\BatchedBridge\NativeModules.js:146:41 in createErrorFromErrorData
node_modules\react-native\Libraries\BatchedBridge\NativeModules.js:95:55 in
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:397:4 in __invokeCallback
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:127:28 in
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:297:10 in __guard
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:126:17 in invokeCallbackAndReturnFlushedQueue
[native code]:null in invokeCallbackAndReturnFlushedQueue
Below is mycode:
Audio.setAudioModeAsync({
allowsRecordingIOS: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false,
staysActiveInBackground:true
});
if (playbackInstance != null) {
await playbackInstance.unloadAsync();
playbackInstance.setOnPlaybackStatusUpdate(null);
playbackInstance = null;
}
const source = require('./assets/alarm.mp3');
const initialStatus = {
// Play by default
shouldPlay: true,
// Control the speed
rate: 1.0,
// Correct the pitch
shouldCorrectPitch: true,
// Control the Volume
volume: 1.0,
// mute the Audio
isMuted: false
};
const { sound, status } = await Audio.Sound.createAsync(
source,
initialStatus
);
console.log(status ,'status');
// Save the response of sound in playbackInstance
playbackInstance = sound;
// Make the loop of Audio
playbackInstance.setIsLoopingAsync(false);
// the Music
playbackInstance.playAsync();