For rxSingle: <https://github.com/Kotlin/kotlinx.c...
# coroutines
k
For rxSingle: https://github.com/Kotlin/kotlinx.coroutines/blob/master/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt#L23 does anyone know why a Job isn't allowed? Even if you do pass it something like val appScope = CoroutineScope(CoroutineName("AppScope")) it thinks there's a job in it so you have to do the same thing as GlobalScope by creating a CoroutineScope object with an empty context:
Copy code
val appScope =
    object : CoroutineScope {
        override val coroutineContext: CoroutineContext
            get() = EmptyCoroutineContext + CoroutineName("AppScope")
    }
k
I think the expectation here is that this is the boundary between coroutines and rx. You’re running coroutines in a way that will be canceled if the Single is disposed of. If you pass a context in with a job, there could potentially be two different avenues for cancellation which would be really unintuitive.
I am unsure why your example doesn’t work though. I don’t know why that would have a Job
k
ahh..yep, that's what I was thinking too, even though in practice, the SupervisorJob() I passed in never would have been cancelled.
yep, the need to use an object was puzzling too.
k
My mental model for this is that this is a coroutine builder function like
runBlocking
. At boundaries like this you’re supposed to exercise more caution. For example, you aren’t supposed to call runBlocking from coroutines code because it can potentially result in deadlock. The compiler won’t/can’t enforce this, though. Likewise here, you’re entering into coroutines world, so it’s a bit strange to try and pass it some information that already exists in coroutine world (namely, CoroutineScope).
👍 1
If you’re just trying to give the
rxSingle
coroutine a name, and truly don’t need to scope, just pass in a name as the coroutine context?
k
yep...just wanted to name it. Basically trying to avoid folks calling GlobalScope in the app by replacing it w/ an AppScope object.
k
Does
Copy code
rxSingle(CoroutineName("Ken Yee")) { ... }
Work?
🤔 1
k
will get back to you on that...on another branch at the moment
k
FWIW here’s the commit the deprecated
CoroutineScope.rxSingle
in favor of a top level one that runs in Globalscope. https://github.com/Kotlin/kotlinx.coroutines/commit/d100a3f753fdfcdbdae5d82b8ef9f01126ad6a7c
k
circling back on this: rxSingle(CoroutineName("something")) That doesn't work because a CoroutineName is not a CoroutineScope. So looks like the solution I have is the right one. TLDR, if you mix rx and coroutines, you can't define a new CoroutineScope...gotta be something that looks like GlobalScope w/ an empty coroutine context.
k
What is your end goal? To name the coroutine run for rx single, or to run
rxSingle
in a scope other than globalscope? The latter is impossible for good reasons, the former is possible by just passing in a coroutine name.
k
more to discourage folks from using Globalscope (and trigger the detekt lint warning). Sounds like best bet it to alias it...
d
rxSingle(CoroutineName("something"))
That doesn't work because a CoroutineName is not a CoroutineScope
No, it does work, I just checked.
rxSingle
doesn't need a
CoroutineScope
, it needs a
CoroutineContext
.
more to discourage folks from using Globalscope (and trigger the detekt lint warning).
How does
rxSingle
help with that? Do you mean that you just want to replace
GlobalScope.launch
with
rxSingle
? This doesn't solve the fundamental problems of
GlobalScope
. Its problem is not a "lint warning", but why the warning is there in the first place: because it's not part of structured concurrency. I suggest not to alias
GlobalScope
but to try to restructure the code so that it doesn't need
rxSingle
or
GlobalScope
.
rxSingle
is strictly for interacting with code that depends on RxJava.
k
This is a codebase that just has a mixture of Rx and Coroutines so we can't get rid of rxSingle usage that easily. In a normal full kotlin android app, using an App scope (scope that lives the lifetime of the app) is correct and it's nice to be able to define an exception handler for it as well as a new name. We're using rxSingle for coroutine interop in older cove that mixes with Rxjava.
👍 1
d
Then
rxSingle(CoroutineName("..."))
is the way to go.
k
we have a lot of code that looks like:
Copy code
fun something(): Single<SomeData> = rxSingle {
that uses the current coroutine scope (which happens to be globalscope) and that's where rxSingle flagged the bad scope issue when I tried to replace all the GlobalScope.launch calls with AppScope. This is so that something() can be used by older Rx code but we want all new code to use coroutines. AppScopes would normally be things like "network is still available" (cancelled when it's lost), etc. besides the full lifetime AppScope.
d
Sorry, could you rephrase that?
something()
is just fine if it's written for consumption by code that uses RxJava, where's the problem?
k
That's correct. The issue I hit was that some folks were doing this inside the rxSingle { GlobalScope.launch { stuff } } So I replaced GlobalScope w/ AppScope and that's what caused rxSingle to complain. It's perfectly valid and I understand why now...just have never hit it before :-)
d
Why do you even need
GlobalScope.
there? You can just do
rxSingle { launch { stuff } }
.
k
Some people here are just learning coroutines (from an RxJava background)...they make mistakes like this 😞 I should probably should add a lint rule for doing that...
d
Sure, this channel is often used for learning; it's good that you asked this question and learned something new.
1
k
Found the bit of code that was the reason I asked:
Copy code
): Single<ColorThievingResult> = rxSingle(AppScopes.getGlobal(context).coroutineContext) {
        buildBillboardGradients(image, imageUrl, rectToSample, rtl)
    }
In that case, the AppScopes wasn't even needed 😞 Will confirm and remove...thanks for the reminder.