【问题标题】:How / when to release the resources acquired by viewHolder in a recyclerView?如何/何时释放recyclerView中viewHolder获取的资源?
【发布时间】:2022-04-02 14:31:00
【问题描述】:

简单来说,我有一个 recyclerView,它的 viewHolders 持有 MediaPlayer 实例。当用户关闭该活动时,我想释放 MediaPlayer 实例获取的资源。但与 onCreateViewHolder 不同的是,我找不到任何 onDestroyViewHolder 方法。我的 viewHolder 实现如下所示:

class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    private val mMediaPlayer: MediaPlayer? = null
    
    internal fun onBind(model: Model) {
        if(model.contentType == "Audio") {
            mMediaPlayer = MediaPlayer()
            mMediaPlayer.setOnPreparedListener(mPreparedListener);
            mMediaPlayer.setOnCompletionListener(mCompletionListener);
            mMediaPlayer.setDataSource(mContext, uri, null)
            mMediaPlayer.prepareAsync()
            // do other stuff

        } else if(mMediaPlayer != null) {
            mMediaPlayer.reset()
            mMediaPlayer.release()
            mMediaPlayer = null
        }
    }
}

我知道 Adapter 类有 onViewDetachedFromWindow 方法。但我不确定这是否适合这里。有人可以告诉我解决方案吗?

【问题讨论】:

  • “我有一个 recyclerView,它的 viewHolders 持有 mediaPlayer 实例”。这是组织代码的一种非常错误的方式。此类资源应由Activity/Fragment持有。
  • @Onik,例如,在聊天应用程序(这是我正在尝试构建的)中,用户可以共享媒体。所以这将在聊天线程中显示和播放。如果无法在recyclerview 中播放音频,如何处理这种情况?这种情况有什么解决方法吗?
  • MediaPlayer 不是 UI 组件,因此不应存储在 UI 组件中。我不经常使用音频,但我的第一直觉是在 Fragment 中保留一个可用的 MediaPlayer 实例,创建一个用于设置新数据源并播放它的函数,然后从适配器中的 UI 组件调用该函数。
  • @Tenfour04,是的,我明白你的意思。甚至当我遇到这种情况时,我也有同感。但是,即使在同一视图中有多个音频,当我们按下播放按钮时,像 facebook/whatsapp 这样的 Messenger 应用程序也可以立即播放音频。如果它将 MediaPlayer 实例存储在 Fragment/Activity 中,那么它必须有一个 mediaPlayer 列表,每个都处于准备状态,具有视图中可见的所有不同音轨的 url。会是这样吗?这是一个更好的解决方案吗?还有其他想法吗?
  • 这听起来像是一个潜在的解决方案,创建一个 MediaPlayerPool 类,这样您就可以独立控制一次预加载多少声音。你可以给它一个限制。因此,当视图出现在屏幕上时,他们会将声音 Uri 提交到 Pool,它可以使用下一个可用的 MediaPlayer 在其上调用 prepare(),如果它处于限制状态,则替换最旧的。您可能希望给它一个专用的处理程序来处理这些请求,以便它可以在后台关闭 UI 线程并保持prepare()start() 调用的顺序。

标签: android kotlin android-recyclerview android-mediaplayer android-lifecycle


【解决方案1】:

您应该在 viewHolder 中创建 releaseResources 的方法,并从 onViewRecycled 调用它,该方法在您的回收器适配器中被覆盖

    override fun onViewRecycled(holder: MediaViewHolder) {
        super.onViewRecycled(holder)
        val videoItem = mediaItems.getOrNull(holder.bindingAdapterPosition)
        holder.releasePlayer(videoItem)
    }

为了在每次片段进入停止状态时调用此方法,您应该将 recyclerview 适配器设置为 null

  override fun onStop() {
        super.onStop()
        currentPage = binding.pager.currentItem
        binding.pager.adapter = null
  }

这将导致在所有 viewHolders 上调用 onViewRecycled

您应该在 onStart() 中将适配器重新设置为 recyclerview

override fun onStart() {
    super.onStart()
    if (binding.pager.adapter == null) {
        binding.pager.adapter = adapter
        binding.pager.setCurrentItem(currentPage, false)
    }
}

此示例在 ViewPager2 上,但也适用于 RecyclerView。

【讨论】:

    猜你喜欢
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 2013-08-03
    • 1970-01-01
    • 1970-01-01
    • 2019-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多