I am creating an app that has several categories of small audio clips. When the user clicks on a category it loads a list of audio files. When a user clicks on an audio file it plays the audio.
I have several people testing my app on iOS. It does not crash for me, but other people are reporting it crashes after going through several audio files. It is not specific audio files that it crashing on and seems rather intermittent. I am using willBlurSubscription to release the memory.
Here is a simplified version of my code for the audio list:
import React, { Component } from 'react';
var Sound = require('react-native-sound');
Sound.setCategory("Playback"); //Needed for audio to play on IOS devices
import {
Image,
ScrollView,
View,
Text,
TouchableOpacity
} from 'react-native';
import files from '../assets/json/files';
export default class AudioList extends Component {
constructor(props) {
super(props)
}
playAudio = (file) => {
console.log(file)
if (this.sound) {
console.log("SOUND")
try {
this.sound.release()
} catch(error){
console.log("A sound release error has occured 111")
}
finally {
this.sound = null
}
}
this.sound = new Sound('audio/' + file, Sound.MAIN_BUNDLE, (error) => {
if (error) {
console.log('error', error);
this.sound = null;
} else {
this.sound.play(()=> {
try {
if (this.sound){
this.sound.release()
}
} catch(error) {
console.log("A sound release error has occured 222")
}
finally {
this.sound = null
}
})
}
})
this.willBlurSubscription = this.props.navigation.addListener(
'blur',
() => {
try {
if (this.sound){
this.sound.release()
}
} catch(error) {
console.log("A sound release error has occured 333")
}
finally {
this.sound = null
}
}
)
}
componentWillUnmount() {
try {
this.willBlurSubscription &&
this.willBlurSubscription.remove &&
this.willBlurSubscription.remove()
} catch(error) {}
finally {
this.willBlurSubscription = null
}
}
render() {
let list = files[this.props.route.params['category_id']]
let audio_list = list.data.map((item, index) => {
return <View key={index}>
{!!item.audio &&
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
{item.audio ?
<TouchableOpacity onPress={()=>this.playAudio(item.audio)}>
<Image source={require('../assets/images/play_button.png')} />
</TouchableOpacity>
:
<View></View>
}
<Text>{item.text}</Text>
</View>
{!!item.audio2 &&
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
{item.audio2 ?
<TouchableOpacity onPress={()=>this.playAudio(item.audio2)}>
<Image source={require('../assets/images/play_button.png')} />
</TouchableOpacity>
:
<View></View>
}
<Text>{item.text2}</Text>
</View>
}
</View>
})
return (
<ScrollView>
<View>
{audio_list}
</View>
</ScrollView>
)
}
}
Sentry Report:
I have now implemented Sentry and on crash I am getting the following in Sentry:
RCTFatalException: Unhandled JS Exception: TypeError: null is not an object (evaluating 'a.sound.play')
Please remember that this is an intermittent problem. The sound file does indeed exist so should not be null. Other times I click on it it does play the file.
I am wondering if the clearing of the previous audio file to load the new one is causing the problem. During that short time it could be null.
The reason for clearing the previous audio file was to eliminate memory leaks of creating so many audio objects.
What am I doing wrong here?