2

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.

2
  • I think you should research and read more document about MVVM, Clean Architecture then read Coroutine's document. There are many issues in your code. Short answer: Move the getWorkItem() into ViewModel, then use LiveData. Commented Oct 18, 2022 at 9:04
  • That is true, I fix the code after someone I do not like how it is done, but I won't change it. That is true I could use flow/livedata and just wait for the data I have done it like that in my other projects. Thanks. Commented Oct 18, 2022 at 9:23

3 Answers 3

1

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 } } 
Sign up to request clarification or add additional context in comments.

4 Comments

return' is not allowed here I need to change it to @launch, but then i don't return false in setOnMenuItemClickListener
@Chris you have brackets wrong. The true should be outside of the launch.
I know, I'm talking about something different. In the way you bring your proposition there is no way of "return@setOnMenuItemClickListener false", but I have just make variable with ifs to change it. Thanks man. it works
@Chris I went over this again and you can. As I understand data?.parentProjectId is not a coroutine value and since getWorkItem relies on it being non-null you can assume if first is null, second will be as well and that is your false.
0

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

This official tutorial may help.

2 Comments

While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
@GabrieleMariotti you mean like including "You can implement the CoroutineScope interface and call launch to start a coroutine within methods of that class." here? Like they did?
0

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.

4 Comments

I think it won't work, because there is no .await() and IMO it starts coroutine and immediately check for parentProject == null with no 100% certanty that will be done.
You're right, you'd need an await(), I updated the answer.
I have the same idea, but the problem is .await should be also done in coroutine so the entire thing should be done with GlobalScope.launch {} which is not nice and also parentProject should be define earlier before this GlobalScope
True, the answer you accepted looks like a better approach!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.