[英]Intermittent crashes with audio in react-native
播放音频时崩溃:
我正在创建一个包含几个不同屏幕的音频剪辑的应用程序。 我们正在使用 Testflight 在 iPhone/iPad 上测试该应用程序,并且在播放音频剪辑时出现间歇性崩溃。 音频剪辑本身似乎没有任何问题,因为它们大部分时间都在工作,而且问题并不总是同一个音频剪辑。
我猜这可能是与new Sound
有关的 memory 泄漏,但我不确定如何对此进行测试。 我还认为我通过确保在声音组件播放后和屏幕卸载时released
声音组件来解决这个问题。
在确保正确清理this.sound
组件时,我是否遗漏了什么?
我的代码:
import React, { Component } from 'react';
var Sound = require('react-native-sound');
Sound.setCategory("Playback"); //Needed for audio to play on IOS devices
import {
Image,
ImageBackground,
SafeAreaView,
ScrollView,
View,
Text,
TouchableOpacity
} from 'react-native';
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() {
/* list and new_list removed for brevity */
/* styles removed for brevity */
let audio_clips = new_list.map((item, index) => {
return <View key={index}>
{item.audio ?
<TouchableOpacity onPress={()=>this.playAudio(item.audio)}>
<Image source={require('../assets/images/audio-icon.png')} />
</TouchableOpacity>
:
<View></View>
}
<Text>{item.text}</Text>
</View>
})
return (
<SafeAreaView>
<Text>{list.category_name}</Text>
<ImageBackground source={require('../assets/images/background.jpg')}>
<ScrollView>
<View>
{audio_clips}
</View>
</ScrollView>
</ImageBackground>
</SafeAreaView>
)
}
}
检查声音是否已经是 object:
if(!this.sound){
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.sound.release()
被调用两次的竞争条件吗? 所以第二次调用 null 并且它崩溃了。 我的意思是在播放声音后和在“模糊”事物监听器处同步调用它。 应该可能会被异常块捕获,但我对 js 没有足够的经验知道这一点..
我认为在加载音频文件并且发生blur
事件时可能会发生崩溃。 而且你没有检查这个 state。 所以当你使用
this.sound = new Sound('audio/' + file, Sound.MAIN_BUNDLE, (error) => ...)
this.sound
变为有效并且音频文件开始加载。 但是会立即发生blur
事件,然后您释放this.sound
。 与此同时,加载完成并尝试播放! 什么都没有,你会崩溃!
解决方案:您必须在模糊事件上同时检查this.sound
和this.sound.isLoaded()
( ref )。
这里只是一个快速的猜测,但你确定将 null 设置为声音的 object 会使其与 memory 不同吗? 可能只是有另一个“指针”指向它。
您是否尝试过删除相同的代码只是为了尝试,如果这可能是问题? this.sound = null 尝试删除 this.sound 或类似的东西?
我知道删除算作不好的优化实践,但在这里可能会对您有所帮助。
这可能会有所帮助,请查看
import React, { Component } from "react";
var Sound = require("react-native-sound");
Sound.setCategory("PlayAndRecord", true);
Sound.setActive(true); // add this
export default class AudioList extends Component {
playAudio = (file) => {
if (this.sound) {
try {
this.sound.release();
} catch (error) {
console.log("A sound release error has occured 111");
} finally {
this.sound = null;
}
}
const pathType =
Platform.OS === "ios"
? encodeURIComponent(Sound.MAIN_BUNDLE)
: Sound.MAIN_BUNDLE;
this.sound = new Sound("audio/" + file, pathType, (error) => {
/* .... */
});
};
componentWillUnmount() {
try {
if (this.sound) {
this.sound.release();
}
} catch (error) {
console.log("A sound release error has occured 333");
} finally {
this.sound = null;
}
}
render() {
/* .... */
}
}
https://rossbulat.medium.com/react-native-how-to-load-and-play-audio-241808f97f61
'react-native-audio 已经超过 2 年没有更新了.. 应该避免...改用 expo-av...' - 从文章中提取
https://docs.expo.io/versions/latest/sdk/audio/
export class Controller {
apiEndpoint = 'https://<your_domain>/audio/load';
audioFemale = new Audio.Sound();
audioMale = new Audio.Sound();
/* resetAudioClips
* stops and unloads any existing audio clips asynchronously,
* without blocking execution
*/
resetAudioClips = async () => {
this.audioFemale.unloadAsync();
this.audioMale.unloadAsync();
}
/* cautiousResetAudioClips
* stops and unloads any existing audio clips asynchronously,
* fetching checking the audio state first.
* Also blocks execution until audio is unloaded.
*/
cautiousResetAudioClips = async () => {
let femaleStatus = await this.audioFemale.getStatusAsync();
let maleStatus = await this.audioMale.getStatusAsync();
if (femaleStatus.isLoaded === true) {
await this.audioFemale.stopAsync()
await this.audioFemale.unloadAsync();
}
if (maleStatus.isLoaded === true) {
await this.audioMale.stopAsync()
await this.audioMale.unloadAsync();
}
}
/* loadClips
* token: some authentication token to your API
* uriFemale: the path to the requested female audio clip
* uriMale: the path to the requested male audio clip
*
* example:
* Controller.loadClips('s!ke9r3qie9au$2kl#d', '/audio/female/a_394.mp3', '/audio/male/a_394.mp3');
*/
loadClips = async (token, uriFemale, uriMale) => {
this.audioFemale.loadAsync({
uri: this.apiEndpoint,
headers: {
token: token,
file: uriFemale,
}
}, {
shouldPlay: false,
volume: 1,
});
this.audioMale.loadAsync({
uri: this.apiEndpoint,
headers: {
token: token,
file: uriMale,
}
}, {
shouldPlay: false,
volume: 1,
});
}
/* playAudioplay
* play audio by gender
*/
playAudio = async (gender) => {
if (gender == 'female') {
this.audioFemale.replayAsync();
} else {
this.audioMale.replayAsync();
}
}
/* stopAudio
* stops all audio
*/
stopAudio = async () => {
await this.audioFemale.stopAsync();
await this.audioMale.stopAsync();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.