问题
I am developing a chat application and there is a specific API so some things i must implement them with a specific way. For example (and the case that i have a problem...)
When i have to display an Image the API says that i have to split the Image in small chunks and store them as a message with a byteArray content. There is also a header message that its body is the messageIds of the fileChunks. So in the RecyclerView inside the onBindViewHolder, when i see a header file message (msgType == 1) then i start a coroutine to fetch the chunkFile messages by the ids, construct the File and then switch to the MainDispatcher, and so the Image with Glide using a BitmapFactory.decodeByteArray. The code is shown below
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
}
}
}
My problem is that when i scroll fast the image that is supposed to be loaded in an item appears in another item.
My first guess is that the Coroutine that started from a specific item didnt complete as soon as the item was recycled so when the coroutine finished it had a reference to a new item, so i added the
holder.itemView.addOnAttachStateChangeListener method as some people commented, to cancel the coroutine when the item is recycled. However i didn't work.
Is there any idea of why that may happens and if there is a better implementation of the proccess according to the specific API...?
来源:https://stackoverflow.com/questions/57952131/starting-a-coroutine-inside-onbindviewholder-creates-a-mess-with-recycler