react-native 中的音频间歇性崩溃
Posted
技术标签:
【中文标题】react-native 中的音频间歇性崩溃【英文标题】:Intermittent crashes with audio in react-native 【发布时间】:2021-03-31 13:31:37 【问题描述】:播放音频时崩溃:
我正在创建一个包含多个不同屏幕的音频剪辑的应用程序。我们正在使用 Testflight 在 iPhone/iPad 上测试该应用程序,并且在播放音频剪辑时出现间歇性崩溃。音频剪辑本身似乎没有任何问题,因为它们大部分时间都在工作,而且问题并不总是同一个音频剪辑。
我猜这可能是与new Sound
相关的内存泄漏,但我不确定如何对此进行测试。我还认为我通过确保在声音组件播放后和屏幕卸载时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>
)
【问题讨论】:
你应该在 yojur 项目中添加 Sentry,它会检测到崩溃并给你提示:docs.sentry.io/platforms/react-native 【参考方案1】:检查声音是否已经是一个对象:
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
)
);
【讨论】:
但是原代码不应该只是覆盖前一个对象,从而不在内存中保留多个声音对象吗? @kojow7 你可以使用 Map这可能是this.sound.release()
被调用两次的竞争条件吗?因此,它第二次被调用为 null 并且它崩溃了。我的意思是在播放声音后和在“模糊”事物监听器处同步调用它。应该可能会被异常块捕获,但我对 js 没有足够的经验知道这一点..
【讨论】:
【参考方案3】:我认为在加载音频文件和blur
事件发生时可能会发生崩溃。你没有检查这个状态。所以当你在使用
this.sound = new Sound('audio/' + file, Sound.MAIN_BUNDLE, (error) => ...)
this.sound
生效并开始加载音频文件。但立即blur
事件发生,你释放this.sound
。与此同时,加载完成并尝试播放!什么都没有,你会崩溃!
解决方案:您必须同时检查 this.sound
和 this.sound.isLoaded()
(ref) 的模糊事件。
【讨论】:
【参考方案4】:React Native:如何加载和播放音频
https://rossbulat.medium.com/react-native-how-to-load-and-play-audio-241808f97f61
'react-native-audio 已经超过 2 年没有更新了.. 应该避免...改用 expo-av...' - 摘自文章
世博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();
【讨论】:
【参考方案5】:这里只是一个快速的猜测,但你确定将 null 设置为 sound 的对象会从内存中删除它吗?可能只是有另一个“指针”。
您是否尝试过删除相同的代码只是为了尝试,如果这可能是问题? this.sound = null 而是尝试 删除 this.sound 或类似的东西?
我知道删除算作不好的优化做法,但在这里可能会对您有所帮助。
【讨论】:
【参考方案6】:这可能有帮助,请查看它
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()
/* .... */
【讨论】:
您能否进一步解释一下最初的问题可能是什么? @kojow7 我认为资源可能还没有释放。我试图提供帮助,希望您的问题能够得到解决。谢谢:-)以上是关于react-native 中的音频间歇性崩溃的主要内容,如果未能解决你的问题,请参考以下文章