使用wx.createInnerAudioContext()中onPlay等监听的奇怪问题观察和解决
发布于 4 年前 作者 ping05 10114 次浏览 来自 官方Issues

问题描述:

  1. 在一个非首页的页面创建了一个全局的 mediaPlayer => wx.createInnerAudioContext() 
  2. 在页面的onload 中设置了 onPlay 、onStop、onEnded、 三种监听方法。
  3. 业务逻辑:a. 在页面设置播放按钮,点击后用自定义方法playMusic 触发 mediaPlayer.play b. 离开页面时候,在onUnload中触发mediaPlayer.stop()
  4. 设置一个标记Index值 =》
  5. 初始 :0 ;
  6. 观察index在下列动作下的赋值

播放按钮被点击:index => 0(赋值);

开始播放onPlay:先console.log(index), 再赋值 1 => index

停止播放onStop:先console.log(index), 再赋值 2 => index

自然播放结束onEnded:先console.log(index), 再赋值 3 => index ;

代码如下:

const mediaPlayer = wx.createInnerAudioContext()
Page({
  data: {
    index: 0,
    music: [
      '这里放置你的音频文件链接'
    ]
  },
  onLoad () {
    mediaPlayer.onPlay(() => {
      console.log('onPlay index:',  this.data.index)
      this.setData({
        index: 1
      })
    })

    mediaPlayer.onStop(() => {
      console.log('onStop index:',  this.data.index)
      mediaPlayer.src = ''
      this.setData({
        index: 2
      })
    })

    mediaPlayer.onEnded(() => {
      console.log('onEnd index:',  this.data.index)
      mediaPlayer.src = ''
      this.setData({
        index: 3
      })
    })
  },
  playMusic () {
    console.log('playMusic'this.data.index)
    this.setData({
      index: 0
    })
    mediaPlayer.src = this.data.music[this.data.index]
    mediaPlayer.play()
  },
  onUnload () {
    mediaPlayer.stop()
  }
})

实操试验: (三种方式)

第一种: 从一个面跳转到本页面,不做任何播放点击,直接返回上页,然后再进入本页,点击播放按钮直至播放自然结束

从上图可以看到,三个监听都各执行了2次。

问题1:onStop在自然播放结束后,执行了两次。按照页面代码的业务逻辑,onStop是在离开页面后触发的,而我们这时候只离开过页面1次,就是在第一次进入后不做任何动作离开时会触发。为什么第二次进入后没有离开,点击播放完毕后,却触发了两次呢??

问题2:onPlay 和 onEnd 被执行了两次,可以理解为,一次点击 和一次播放结束,触发了两次进入页面产生的onPlay 和 onEnd 。

问题3:需要注意的是 从上述例子可以看出,当我们离开页面的时候,wx.createInnerAudioContext() 创建的实例仍然是存在的,并没有被销毁。

问题4:那么我们在onUnload的时候,用mediaPlayer.destroy()进行销毁,会发生什么情况呢? 实际是,当你离开页面的时候销毁了播放实例,在再次进入(wx.navigateTo)该页面时候, 播放器已经不存在了,也就是说,触发一次wx.createInnerAudioContext() 的监听,只要触发一次,会一直存在,而为了避免这个情况,你如果在离开页面时用destroy去销毁,那么很抱歉,没有下一次了,因为对于被销毁的实例来说 destroy也是一直存在的,这看上去还真是逻辑很通畅呢。

第二种:从一个面跳转到本页面,点击播放按钮进行播放,在中途返回上一页,再进入本页,再点击播放,直到自然结束

上图中我们可以看到,

(1)红框内三次输出index是第一次进入后点击播放的操作结果。 index在这个过程中分别从0 => 1 =>2 .注意,index为2 是onStop也就是离开页面时候的赋值,上图的值均为赋值前的状态。 划重点: 在第一次播放离开后,index在离开这一刻,在onStop的触发下,被赋值为2。为什么这里要划重点呢?看下面你就知道诡异在哪里了。

(2)蓝框内是第二次进入页面后 点击播放,直到播放结束的index变化。按照代码逻辑,当我们点击按钮playMusic之后,index被赋值为0,然后再进行播放。而在onPlay的时候,我们是再一次输出index,按照正常逻辑,index此时应该是保持playMusic之后的状态0,但是你看到,在这里有两个onPlay,依次输出了2 和0两个不同的值。也就是说,this.data.index 在此刻有两个值。这是不是很诡异?我的理解是:其中那个2,是上一次离开页面的时候 onStop赋给index的那个2,这就回应了上面段落中的红色重点,但是如果你在页面的onload中输出this.data.index ,却永远是初始值0,你看不到那个2,那么这个2是怎么出来的,它究竟隐藏在哪里呢?这个需要腾讯的高科技人员来解读一下。

以上的监听疑惑,在业务上产生了各种麻烦(也许本人是理解不透),那么如何避免或者如何解决上面的问题呢?

我的办法是: 不要在页面全局设置播放组件,把播放组件的设置放到本页面的onload中,并把这个组件保存到本页面的data里面:

 onLoad () {
    this.setData({
      mediaPlayer: wx.createInnerAudioContext()
    })
.....

在使用该组件时,用this指向 如 this.data.mediaPlayer.play() 、 this.data.mediaPlayer.onEnd …

如此,不存在多次监听和指向困惑。

我感觉,作用域,一直是小程序最大的一个没有能很好解决的问题,包括数据的双向绑定一直不能实现,也是属于作用域的实现方式。

总体来说,小程序框架距离一流的技术框架还相当遥远,使用小程序框架,有一种从六缸涡轮上下来换成4缸自然吸气的感觉。它一直存在很多问题,但却很少给程序员一种耳目一新的技术提升感

回到顶部