繁体   English   中英

react-native 中的音频间歇性崩溃

[英]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.soundthis.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() {
    /* .... */
  }
}

React Native:如何加载和播放音频

https://rossbulat.medium.com/react-native-how-to-load-and-play-audio-241808f97f61

'react-native-audio 已经超过 2 年没有更新了.. 应该避免...改用 expo-av...' - 从文章中提取

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM