Hello, anyone can help me with this error? ```Exce...
# coroutines
g
Hello, anyone can help me with this error?
Copy code
Exception in thread "DefaultDispatcher-worker-6 @track-session/cu#80" java.lang.NullPointerException: Cannot invoke "kotlinx.coroutines.flow.Flow.collect(kotlinx.coroutines.flow.FlowCollector, kotlin.coroutines.Continuation)" because "this.$this_unsafeTransform$inlined" is null
	at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1.collect(SafeCollector.common.kt:113)
	at kotlinx.coroutines.flow.FlowKt__CollectKt.collect(Collect.kt:30)
	at kotlinx.coroutines.flow.FlowKt.collect(Unknown Source)
	at gragas.play.TrackSession.playRegisteredTracks(TrackSession.kt:117)
	at gragas.play.TrackSession.access$playRegisteredTracks(TrackSession.kt:38)
	at gragas.play.TrackSession$3.invokeSuspend(TrackSession.kt:58)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [CoroutineName(track-session/cu), CoroutineId(80), "track-session/cu#80":StandaloneCoroutine{Cancelling}@6465a67d, Dispatchers.Default]
This is the source of playRegisteredTracks
Copy code
private suspend fun playRegisteredTracks() {
  queue
    .onEach { song ->
      player.playTrack(song)
    }
    .collect()
}
And this is the full source of TrackSession.kt https://gist.github.com/2cc063411159091c45c3a3e1d4cc8155
n
Quite a few thoughts here: Member properties and init blocks run in order. The init block is above the
queue
property so the coroutine calling
playRegisteredTracks
is launched before
queue
is assigned. Similarly, the init block is above the
connection
property so
if (!::connection.isInitialized) {
is will always be true. Many view launching coroutines during construction as an anti-pattern and would instead suggest something more like:
Copy code
class TrackSession private constructor(...) {
    companion object {
        operator fun invoke(..., scope: CoroutineScope): TrackSession =
            TrackSession(...).also { //Constructor just creates the instance
                launchBackgroundWork(scope)
            }
        }
(I've never really had a problem with launching coroutines during construction but seeing this error reminds me how easy it is to mix up ordering so maybe I'll start treating it as an anti-pattern) It's also a widely recognized anti-pattern to implement an interface just to avoid typing in the implementation. You don't really plan on calling
myTrackSession.launch { ... }
from other classes do you so why be a
CoroutineScope
? Just have a scope property and call
scope.launch
. Finally, how do those coroutines end? You aren't passing in an external scope (so some other owner would cancel the coroutine) and you have no
close
method to shut down the work. If this is a singleton (created only one ever and then alive for entire process) then it's fine, but otherwise, it's probably better to end them somehow. GC can sometimes save you from lingering coroutines but it's not something I'd recommend relying on.
The init block is above the
queue
property so the coroutine calling
playRegisteredTracks
is launched before
queue
is assigned.
I wasn't really clear but I suspect this is your issue. That
queue
is null because it hasn't been assigned yet.
g
Hmmm, thank you! The final implementation ended like this https://gist.github.com/3c9b3fc5b1d2fb32db26844105f36cf9
2512 Views