matt tighe
01/11/2019, 7:07 PM@Test
fun `Submits app selection if selections can be made`() {
mainActivityViewModel.submitAppSelection(selectedApp)
runBlocking {
verify(mockAppsStartupFsm).submitEvent(AppSelected(selectedApp))
}
}
class MainActivityViewModel {
fun submitAppSelection(app: App) {
if (!selectionsCanBeMade()) return
lastSelectedApp = app
val coroutineScope = CoroutineScope(Dispatchers.Default)
coroutineScope.launch { appsStartupFsm.submitEvent(AppSelected(app)) }
}
}
AppsStartupFsm#submitEvent
is a suspending function.
Changing the runBlocking scope to be function level doesn’t seem to help. Is there something I’m missing with verification of suspending functions?groostav
01/11/2019, 10:10 PMsubmitEvent
call to ensure its gone off.
As the untaggable Allen Wang suggested, you could return a `Job?`:
fun submitAppSelection(app: App): Job? {
if (!selectionsCanBeMade()) return null
return launch { app.submitEvent(AppSelected) }
}
then, in your test, call mainActivity.submit()?.join()
matt tighe
01/11/2019, 10:12 PMdelay
for some short amount in the test to ensure that the coroutine is launched?groostav
01/11/2019, 10:13 PMmatt tighe
01/11/2019, 10:14 PMgroostav
01/11/2019, 10:14 PMlaunch(CoroutineDispatchers.Unconfined)
matt tighe
01/11/2019, 10:14 PMgroostav
01/11/2019, 10:14 PMunconfined
will cause the launch
block to start immediatley, and only return back to the caller of launch
after the first suspension pointis the way you’re solving the issue of parallelism just by using the main dispatcher in both places?Exactly, through a great deal of effort I can honeslty say this is the best approach. Start off writing single-threaded concurrent code. Parallelize when you need the performance.
that’s kind of out of the question, as that suspension function sometimes does work that cannot be handled on Android’s main threadthis is really odd. This style of suspend-funs-may-return-fast is in C# but was intentionally made more difficult (ie removed) in kotlinx because of the subtle concurrency bugs it can introduce. Typically we statically know if you will suspend or not, the runtime does not get a say in that.
submitEvent
function, is it blocking or is it suspending? Is that an android API call?matt tighe
01/11/2019, 10:16 PMgroostav
01/11/2019, 10:16 PMmatt tighe
01/11/2019, 10:17 PMsubmitEvent
is technically marked as a suspending function, but it never actually suspends anything. i guess i’ve been using the suspend modifier to also indicate functions that need to happen off the main threadgroostav
01/11/2019, 10:17 PMJob?
is a good quick fix, theres a bunch of places in my code where I've had to do that, particularly when old java
is involved. But I think you might be better off doing a withContext
and just making submitAppSelection
tagged as suspend
matt tighe
01/11/2019, 10:18 PMgroostav
01/11/2019, 10:20 PMmatt tighe
01/11/2019, 10:22 PMsubmitEvent
returns (not suspends) will submitAppSelection
return.`groostav
01/11/2019, 10:26 PMwithContext
never does anything in parallel, it will return only when the block inside it returns. Thus your function is made synchronous. It suspends, and it does not hold on to the Main
thread because the thing that might block (the submitEvent
) call is not on the MainThread, its on a worker thread.println("before withContext!")
withContext(Dispatchers.Anything){
println("before submitEvent!")
submitEvent()
println("after submitEvent!")
}
println("after withContext!")
you would only ever see the exact sequence
before withContext!
before submitEvent!
after submitEvent!
after withContext!
I think you should follow thatis technically marked as a suspending function, but it never actually suspends anything. i guess i’ve been using the suspend modifier to also indicate functions that need to happen off the main threadsubmitEvent
suspend
up with a withContext
. This is exactly what I do with my database facade.
class SuspendingTable {
suspend fun get(id: UUID): T {
return withContext(<http://Dispatchers.IO|Dispatchers.IO> + Trace){
backingDb.getBlocking(id)
}
}
}
matt tighe
01/11/2019, 10:40 PMsubmitEvent
function with withContext(Dispatchers.Default)
i can also remove the withContext
call from submitAppSelection
, correct?