#kotlin #kotlin-coroutines #kotlin-android
Вопрос:
Я внедрял viewHolderScope
для сбора потоков в RecyclerView.ViewHolder
s. Это начнется с onBindViewHolder
и будет отменено onRecycleViewHolder
. Но наряду с этим требованием, он также должен автоматически отменяться, когда окружение lifecycleScope
заканчивается в случае Activity
или окружение viewLifecycleScope
заканчивается в случае Fragment
. Итак, как я могу наследовать от этих областей?
interface ScopeProvider { val scope: CoroutineScope } val viewHolderScope = CoroutineScope(ScopeProvider.scope.coroutineContext)
Работает ли это так, как задумывалось?
Редактировать: Поскольку многие спрашивают , почему я собираю потоки ViewHolder
, здесь я расскажу, что я пытался сделать и почему я решил это сделать.
Я создавал приложение для чата. В этом случае рассмотрим экран списка чатов. Там я хотел показать, активен ли пользователь или нет с помощью зеленой точки (например, facebook). Для этого, если мне нужно собирать потоки ViewModel
, я думаю, что мне нужно собирать потоки для всех пользователей в списке чатов, а не только для тех, которые видны на экране. Поэтому я подумал, что вместо этого я мог бы собирать потоки сами по ViewHolder
себе, начиная onBind
и заканчивая вскоре после onRecycle
этого . В любом случае, у него был определенный жизненный цикл, и поэтому я подумал viewHolderScope
, что его можно реализовать.
Edit2: Весь поток статуса пользователя.
lt;ViewHoldergt; viewHolderScope.launch { chatStatusObserver.statusFlow(getItem(bindingAdapterPosition)!!).collect { // update view } } lt;Fragmentgt; private val chatStatusObserver = object : ChatStatusObserver { override fun statusFlow(chat: Chat) = model.statusFlow(chat) } lt;ViewModelgt; fun statusFlow(chat: Chat) = when (chat.type) { ChatType.PRIVATE -gt; repo.getUserStatus(chat.receiverId) ChatType.GROUP -gt; repo.getGroupStatus(chat.receiverId) } lt;Repositorygt; @OptIn(ExperimentalCoroutinesApi::class) fun getUserStatus(userId: String) = callbackFlow { val userStatusListener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val timestamp = snapshot.getValuelt;Longgt;()!! 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) } }
Помимо вышеупомянутого потока, есть только этот вход ViewModel
, который получает поток подкачки из RoomDao
-gt; gt; Repository
и передает его в Fragment
-gt; gt; PagedAdapter
.
val pagedChatsFlow = repo.getPagedChatsFlow().cachedIn(viewModelScope)
Комментарии:
1. Есть ли какая-то особая причина для сбора потока внутри держателя вида? Какую функциональность вы пытаетесь создать?
2. Я рекомендую вам отправлять уже собранные данные владельцам просмотров (например, от модели просмотра или докладчика), вам следует избегать расширения их ответственности
3. Это совершенно плохое решение. Как сказал Стейрикс, вы должны создать UiModels в своей виртуальной машине или презентаторе и установить их в свой адаптер.
4. @ArpitShukla, я обновил свой вопрос. Пожалуйста, проверьте и скажите, что может сработать в этом случае.
5. @Steyrix, я обновил свой вопрос. Пожалуйста, проверьте и скажите, что может сработать в этом случае.