I was implementing viewHolderScope
for collecting flows in RecyclerView.ViewHolder
s. It would start from onBindViewHolder
and gets cancelled onRecycleViewHolder
. But along with this requirement, it should also automatically get cancelled when the surrounding lifecycleScope
ends in case of Activity
or surrounding viewLifecycleScope
ends in case of Fragment
. So, how can I inherit from those scopes?
interface ScopeProvider {
val scope: CoroutineScope
}
val viewHolderScope = CoroutineScope(ScopeProvider.scope.coroutineContext)
Does this work as intended?
Edit: Since many are asking why I am collecting flows in ViewHolder
, here I will tell what I was trying to do and why I choose to do that.
I was building a chat application. In that, consider the chats list screen. There I wanted to show if a user is active or not with a green dot (like facebook). For this, if I have to collect flows in ViewModel
, then I think I have to collect flows for all the users in the chats list and not just the ones visible on screen. So I thought instead I could collect flows in ViewHolder
itself starting onBind
and ending short after onRecycle
. Anyway it had a defined lifecycle, and so I thought a viewHolderScope
can be implemented.
Edit2: Entire flow of user status.
<ViewHolder>
viewHolderScope.launch {
chatStatusObserver.statusFlow(getItem(bindingAdapterPosition)!!).collect {
// update view
}
}
<Fragment>
private val chatStatusObserver = object : ChatStatusObserver {
override fun statusFlow(chat: Chat) = model.statusFlow(chat)
}
<ViewModel>
fun statusFlow(chat: Chat) = when (chat.type) {
ChatType.PRIVATE -> repo.getUserStatus(chat.receiverId)
ChatType.GROUP -> repo.getGroupStatus(chat.receiverId)
}
<Repository>
@OptIn(ExperimentalCoroutinesApi::class)
fun getUserStatus(userId: String) = callbackFlow {
val userStatusListener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val timestamp = snapshot.getValue<Long>()!!
val status =
if (timestamp == -1L) UserOnline
else UserOffline(timestamp)
trySend(status).onClosed {
throw it ?: ClosedSendChannelException("Channel was closed normally")
}
}
override fun onCancelled(error: DatabaseError) {
trySend(Unavailable).onClosed {
throw it ?: ClosedSendChannelException("Channel was closed normally")
}
Log.e(TAG, "onCancelled: ${error.code} : ${error.message}", error.toException())
}
}
trySend(Pending).onClosed {
throw it ?: ClosedSendChannelException("Channel was closed normally")
}
Firebase.database.reference.child(NODE_STATUS).child(userId)
.addValueEventListener(userStatusListener)
awaitClose {
Firebase.database.reference.child(NODE_STATUS).child(userId)
.removeEventListener(userStatusListener)
}
}
Apart from the above flow, there is only this in ViewModel
, which gets paged flow from RoomDao
-> Repository
and passes it to Fragment
-> PagedAdapter
.
val pagedChatsFlow = repo.getPagedChatsFlow().cachedIn(viewModelScope)