chrisu.chrisu
chrisu.chrisu

Reputation: 147

What is the substitute for runBlocking Coroutines in fragments and activities?

It is recommended to not use GlobalScope and runBlocking. I have implemented changes in order to this topic: End flow/coroutines task before go further null issue

However it doesn't work well as previously with runBlocking. In brief icon doesn't change, data is not on time. My case is to change icon depending on the boolean.

usecase with Flow


class GetNotificationListItemDetailsUseCase @Inject constructor(private val notificationDao: NotificationDao): BaseFlowUseCase<Unit, List<NotificationItemsResponse.NotificationItemData>>() {
    override fun create(params: Unit): Flow<List<NotificationItemsResponse.NotificationItemData>> {
        return flow{
            emit(notificationDao.readAllData())
        }
    }
}

viewmodel

    val actualNotificationList: Flow<List<NotificationItemsResponse.NotificationItemData>> = getNotificationListItemDetailsUseCase.build(Unit)
    

fragment

    private fun getActualNotificationList() : Boolean {
        lifecycleScope.launch {
            vm.actualNotificationList
                .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
                .collect { response ->
                    notificationData.value = response
                    val notificationDataString = notificationData.value.toString()
                    val stringToCheck = "isRead=false"
                    isNotificationNotRead = (notificationDataString.contains(stringToCheck))
                }
        }
        return isNotificationNotRead
    }

on method onViewCreated I have initToolbar to check if it's true and make action, with runBlokcing worked.

fun initToolbar{
        if (onReceived) {
            Log.d("onReceivedGoes", "GOES IF")
        } else {
            Log.d("onReceivedGoes", "GOES ELSE")
            getActualNotificationList()
        }
        onReceived = false

        val item = menu.findItem(R.id.action_notification_list)
        when {
            isNotificationNotRead && !isOutcomed -> {
                item.setIcon(R.drawable.image_icon_change)
            }
}

coroutine job before change, it worked well

        val job = GlobalScope.launch {
            vm.getNotificationListItemDetailsUseCase.build(Unit).collect {
                notificationData.value = it
                val notificationDataString = notificationData.value.toString()
                val stringToCheck = "isRead=false"
                isNotificationNotRead = (notificationDataString.contains(stringToCheck))
            }
        }
        runBlocking {
            job.join()
        }
    }

Another question is I have the same thing to do in MainActivity, but I do not use there a flow just suspend function.

UseCase

class UpdateNotificationListItemUseCase @Inject constructor(private val notificationDao: NotificationDao): BaseUpdateBooleanUseCase<Int, Boolean, Boolean, Boolean, Unit>() {
    override suspend fun create(itemId: Int, isRead: Boolean, isArchived: Boolean, isAccepted: Boolean){
        notificationDao.updateBooleans(itemId, isRead, isArchived, isAccepted)
    }
}

MainActivity

            val job = GlobalScope.launch { vm.getIdWithUpdate() }
            runBlocking {
                job.join()
            }

MainViewmodel

suspend fun getIdWithUpdate() {
        var id = ""
        id = notificationAppSessionStorage.getString(
            notificationAppSessionStorage.getIncomingKeyValueStorage(),
            ""
        )
        if (id != "") {
           
            updateNotificationListItemUseCase.build(id.toInt(), true, false, false)
        }
    }
}

EDIT1:

collect in fragments works perfectly, thanks

What about MainActivity and using this usecase with suspend fun without flow.

I have read documentation https://developer.android.com/kotlin/coroutines/coroutines-best-practices

        val IODispatcher: CoroutineDispatcher = Dispatchers.IO
        val externalScope: CoroutineScope = CoroutineScope(IODispatcher)
            suspend {
                externalScope.launch(IODispatcher) {
                    vm.getIdWithUpdate()
                }.join()
            }

Second option, but here I do not wait until job is done

            suspend {
                withContext(Dispatchers.IO) {
                    vm.getIdWithUpdate()
                }
            }

What do you think about it?

Upvotes: 1

Views: 2071

Answers (1)

Sergio
Sergio

Reputation: 30655

You can try to update the icon in the collect block:

private fun getActualNotificationList() = lifecycleScope.launch {
        vm.actualNotificationList
            .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
            .collect { response ->
                notificationData.value = response
                val notificationDataString = notificationData.value.toString()
                val stringToCheck = "isRead=false"
                val isNotificationNotRead = (notificationDataString.contains(stringToCheck))

                val item = menu.findItem(R.id.action_notification_list)
                when {
                    isNotificationNotRead && !isOutcomed -> {
                        item.setIcon(R.drawable.image_icon_change)
                    }
                }
            }
}

Using runBlocking you are blocking the Main Thread, which may cause an ANR.

Upvotes: 1

Related Questions