Home > Blockchain >  How to use coroutines without runBlocking?
How to use coroutines without runBlocking?

Time:10-18

Using runBlocking I block main thread which is not proper. I would like to implement it more efficient.

    private fun getWorkItem(parentProjectId: Int?): WorkItemRoom? {
        val workItemRepository by inject<WorkItemRepository>()
        var returnParent: WorkItemRoom? = null
        runBlocking {
            if (parentProjectId != null) {
                returnParent = workItemRepository.getWorkItem(parentProjectId)
            }
        }
        return returnParent
    }

This is where I use it:

                val parentProject = getWorkItem(parentProjectId) ?: return@setOnMenuItemClickListener false

The entire code surrounding with navigation, showing button etc. which is related to this parentProject data.

    private fun updateMenuItems() {
        with(requireContext()) {
            val data = viewModel.data.value?.dataOrNull

            editMenuItem?.isVisible = hasInternetConnection() && (data?.isProjectEditable ?: false)
            editMenuItem?.setOnMenuItemClickListener {
                findNavController().navigate(
                    ProjectDetailsFragmentDirections.actionProjectDetailsFragmentToWorkItemFragment(
                        workItemType = WorkItemType.PROJECT,
                        workItemId = args.projectId,
                        requestKey = workItemRequestKey
                    )
                )
                true
            }

            goToParentMenuItem?.isVisible = data?.parentProjectId != null
            goToParentMenuItem?.setOnMenuItemClickListener {
                val parentProjectId = data?.parentProjectId ?: return@setOnMenuItemClickListener false
                val parentProject = getWorkItem(parentProjectId) ?: return@setOnMenuItemClickListener false

                findNavController().navigate(
                    ProjectDetailsFragmentDirections.actionProjectDetailsFragmentSelf(
                        projectId = parentProjectId,
                        isArchived = parentProject.workItemState.isClosed
                    )
                )
                true
            }
        }
    }

And finally this is my suspend function

class GetWorkItemByIdUseCase(private val workItemDao: WorkItemDao) :
    BaseUseCase<Int, WorkItemRoom>() {
    override suspend fun create(id: Int): WorkItemRoom = workItemDao.getWorkItemById(id)
}

How would you change it to get rid of runBlocking? I have tried some solutions, but what only worked is runBlocking. Basically I need to wait for returnParent value when I use it in next lines.

CodePudding user response:

This should work but the question/runBlocking suggests a deeper misunderstanding of how coroutines work. Coroutines are leveraging being highly reactive and not asynchronous.

goToParentMenuItem?.setOnMenuItemClickListener {
    lifecycleScope.launch {
        val parentProjectId = data?.parentProjectId ?: return@setOnMenuItemClickListener false
        val parentProject = getWorkItem(parentProjectId) ?: return@setOnMenuItemClickListener false

        findNavController().navigate(
            ProjectDetailsFragmentDirections.actionProjectDetailsFragmentSelf(
                    projectId = parentProjectId,
                    isArchived = parentProject.workItemState.isClosed
            )
        )
    }
    true
}

private suspend fun getWorkItem(parentProjectId: Int?): WorkItemRoom? = withContext(Dispatchers.IO) {
    val workItemRepository by inject<WorkItemRepository>()
    var returnParent: WorkItemRoom? = null
    if (parentProjectId != null) {
        workItemRepository.getWorkItem(parentProjectId)
    } else {
        null
    }
}

CodePudding user response:

You can implement the CoroutineScope interface and call launch to start a coroutine within methods of that class.

This official tutorial may help.

CodePudding user response:

I'd suggest simply marking getWorkingItem() with suspend, which will allow it to call other suspending functions without having to invoke a coroutine builder function. You can then use your ViewModel's scope to launch a coroutine to call getWorkingItem():

val parentProject = viewModelScope.async {
  getWorkItem(parentProjectId)
} 
if (parentProject.await() == null) return@setOnMenuItemClickListener false

EDIT: Added the await() call on parentProject.

  • Related