``` private fun fetchData() = viewModelScope.launc...
# coroutines
v
Copy code
private fun fetchData() = viewModelScope.launch(dispatcher) {
        repository.fetchData()
                .onEach { 
                   //success
                }
                .catch { Timber.e(it) }
                .launchIn(this)
    }
Why is this still crashing my app when an exception occurs? should
catch
be catching that and not crashing?
z
What's the exception?
v
the exception in this case was just a network error thrown by the repository. Using CoroutineExceptionHandler works as expected and catches the error without crashing the app
I figured
catch
would be equivalent
z
Hm, I would think so too.
Can you put a breakpoint in
catch
and see if it's even getting the exception?
v
yes im trying that now. I think it is getting the exception
ok nvm it is not. breakpoint is not being hit
but the exception is being thrown by
fetchData
network call so catch should definitely be catching that I think...
its also logging
FATAL EXCEPTION: DefaultDispatcher-worker-3
before crashing
z
Huh, weird
v
guess ill stick to CoroutineExceptionHandler for now? This definitely seems like a bug though
z
I don't think this is related, but one odd thing about your code is you're launching a coroutine that doesn't do anything but immediately launch another coroutine. You can get rid of one of those (eg just call
collect
directly instead of
onEach/launchIn
, and surround the collect with a try/catch)
But yea if you can isolate and reproduce, I would file a bug.
v
hmm I switched to using
onEach
instead of
collect
because I had some cases where I needed multiple collects in one launch
Copy code
private fun subscribeToViewModel() = lifecycleScope.launch(Dispatchers.Main) {
        viewModel.viewState.collect { onViewStateEvent(it) }
        viewModel.navigationEvents.collect { onNavigationEvent(it) }
    }
I thought this was equivalent?
z
Nope, in that case
navigationEvents
won’t start collecting until
viewState
finishes.
But then you can just do
viewModel.viewState.onEach{…}.launchIn(lifecycleScope)
, you don’t need the outer call to
launch{}
v
oh I see you dont need to call the launchIn at all... ok I think its possible the exception was for some reason being propagated to the outer launch that had no exception handler
so if I remove the outer launch, it says "fetchData should only be called from a suspend function" even though I have the launchIn
seems like you still need the outer launch
z
fetchData
shouldn’t be a suspend function if it’s returning a
Flow
.
v
Oh ya you are probably right. It's a bit of a messy implementation as I wasnt sure the best way to handle this. I need to fetch data from the API, store it in the DB, then return a flow from the DB to listen for further changes later on
z
v
The suspend part was so that the API call can be made. Maybe there's a better way to handle this to just return flow?
I have not seen that but I'll take a look!
z
That can all be done inside the Flow itself, but requires multicasting. That Store library does all that for you though.
v
That looks really nice but would require a bit of refactoring to implement in my current project. Do you know how to handle this without store?
z
Something like
Copy code
flow {
  val data = fetchData() // suspending call
  storeDataInDb(data) // suspending call
  val dbStream: Flow<…> = streamDataFromDb() // not suspending
  emitAll(dbStream)
}
v
Ok awesome! I knew there was a way to make this a flowable but have only just recently started using flowables and wasn't sure exactly how
s
The
catch
handles an exception thrown by the returned Flow. From this thread, I understand that the
repository.fetchData()
throws the exception? If that's the case, the
catch
won't handle this, since it'll only handle exceptions from the returned Flow; no Flow is returned, an exception is thrown instead inside the
viewModelScope.launch
call.
z
Ah, you’re right. I didn’t realize
fetchData
was throwing the exception directly.
v
ah ya that makes more sense. So this will work fine right? because the exceptions happen within the flow
Copy code
flow {
  val data = fetchData() // suspending call
  storeDataInDb(data) // suspending call
  val dbStream: Flow<…> = streamDataFromDb() // not suspending
  emitAll(dbStream)
}
ok ya just tested it, this works as expected
z
LG