首页 > 解决方案 > 取消 onViewDetachedFromWindow 内特定 ViewHolder 的特定协程

问题描述

(已编辑问题......)我正在开发一个聊天应用程序,并且有一个特定的 API,所以有些事情我必须以特定的方式实现它们。例如(以及我有问题的情况......)

当我必须显示图像时,API 说我必须将图像分成小块并将它们存储为带有 byteArray 内容的消息。还有一个header message,它的body是fileChunks的messageIds。因此,在 onBindViewHolder 内的 RecyclerView 中,当我看到头文件消息(msgType == 1)时,我启动一个协程以通过 id 获取 chunkFile 消息,构造文件然后切换到 MainDispatcher,因此图像与使用 BitmapFactory.decodeByteArray 滑动。代码如下所示

messageItem.message?.msgType == MSG_TYPE_FILE -> {
     holder.sntBody.text = "Loading file"

     val fileInfo = Gson().fromJson(URLDecoder.decode(messageItem.message?.body, "UTF-8"), FileInformation::class.java)

     job = chatRoomAdapterScope.launch(Dispatchers.IO) {
    // i get the messageIds of the chunks from Header message
    val segSequence = fileInfo.seg.split(",").map { it.toLong() }

    // i get the fileChunks from Database
    val fileChunks = AppDatabase.invoke(mContext).messageDao().getMessageById(segSequence)
    val compactFile = ByteArrayOutputStream()

    // Reconstruct the file
    for (chunk in fileChunks)
        compactFile.write(Base64.decode(chunk.fileBody, Base64.DEFAULT))

        withContext(Dispatchers.Main) {
            val bitmapOptions = BitmapFactory.Options().apply {
                inSampleSize = 8
            }

        Glide.with(mContext).asBitmap()
            .load(BitmapFactory.decodeByteArray(compactFile.toByteArray(), 0, compactFile.size(), bitmapOptions)!!)
            .fitCenter()
            .into(object : SimpleTarget<Bitmap>() {
                 override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                    holder.sntImageView.setImageBitmap(resource)
                    holder.sntImageView.visibility = View.VISIBLE
                 }
             })
              holder.sntBody.text = fileInfo.filename
       }
    }
}

我的问题是,当我快速滚动时,应该在一个项目中加载的图像出现在另一个项目中。我的第一个猜测是,从特定项目开始的协程在项目被回收后并没有完成,所以当协程完成时,它引用了一个新项目,所以我添加了 holder.itemView.addOnAttachStateChangeListener 方法,正如一些人评论的那样. 然而我没有工作。是否知道为什么会发生这种情况,以及根据特定的 API 是否有更好的过程实现......?

标签: androidkotlinandroid-recyclerviewkotlin-coroutines

解决方案


我认为您可以使用View.OnAttachStateChangeListener它:

override fun onBindViewHolder() {
    if(messageType == TYPE_FILE) {
        val job = chatRoomAdapterScope.launch(Dispatchers.IO) {
            val fileChunks = AppDatabase.invoke(MyApplication.instance).messageDao()
                .getMessageByMessageId(segSequence)
            // do some heacy work with the fileChunks
            withContext(Dispatchers.Main) {
                // holder set up
            }
        }
        holder.itemView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
            override fun onViewDetachedFromWindow(view: View) {
                view.removeOnAttachStateChangeListener(this)
                job.cancel()
            }

            override fun onViewAttachedToWindow(p0: View?) {}
        })
    }
}

推荐阅读