Hi! I'm pretty knew to Kotlin coroutines and gRPC,...
# grpc
j
Hi! I'm pretty knew to Kotlin coroutines and gRPC, my server code is generated with grpc/grpc-kotlin and I struggle a bit to understand how I can even use the first coroutine explained in Kotlin's documentation in my gRPC server. Say I have the following server method (from the Kotlin gRPC Quick start):
Copy code
private class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() {
    override suspend fun sayHello(request: HelloRequest) = helloReply {}
}
And I want to write the following code at the beginning of this method (from Kotlin's documentation):
Copy code
launch {
    delay(1000L)
    println("World!")
}
println("Hello")
If I simply copy-paste this code in my method,
launch
isn't recognized as a valid method because it should by used on a coroutine scope. If I wrap my code with a
coroutineScope
like this:
Copy code
override suspend fun sayHello(request: HelloRequest) = coroutineScope {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")

    helloReply {}
}
It builds, the code is even executed when I make a call from the client, however the server returns an
UNKNOWN
code before the
launch
job can even finish and the
HelloReply
is not used. So I think I shouldn't create my own coroutine scope and I should use a scope already defined by the server, but I don't find anything related to a "default gRPC coroutine scope" in the API reference. I must be missing something really obvious, can someone help me run this really basic code please? 🙏
a
Im extremely new to grpc as well so I don't know about that but for Coroutine I would try to have my class implement CoroutineScope, put
override val coroutineContext = Dispatchers.Default
inside your constructor. Then you'll be able to use
launch(coroutineContext) { }
j
actually, a context is already provided in the parent classes with the property `context`, you put me on the right path, thank you!
cheers 1
j
@Alexandre Brown implementing
CoroutineScope
is a discouraged approach now. When you need custom scopes, it's preferred to just declare a private property of type
CoroutineScope
instead of implementing the interface (and don't forget to cancel it according to the lifecycle of your class)
j
personally I just went with this:
Copy code
override suspend fun sayHello(request: HelloRequest) = coroutineScope {
    launch(context) {
        delay(1000L)
        println("World!")
    }
    println("Hello")

    helloReply {}
}
The difference with my original code is
launch(context) {
instead of
launch {
a
@Joffrey Interesting, why is this discouraged now exactly? Could you share some resources I'd love it! Thanks
j
From the docs:
Manual implementation of this interface is not recommended, implementation by delegation should be preferred instead.
CoroutineScope
 should be declared as a property on entities with a well-defined lifecycle that are responsible for launching children coroutines.
At least for public classes, implementing the interface breaks the single-responsibility principle. Also, with the introduction of the CoroutineScope() factory function it is less boilerplate to just call the factory and assign a property. This article also describes a bit the "why not implement it": https://proandroiddev.com/why-your-class-probably-shouldnt-implement-coroutinescope-eb34f722e510 If that's any hint, Android also uses the property approach with the built-in
viewModelScope
and
lifecycleScope
.
🙏 1
a
Thanks, I'll pass it to the constructor and put it private so that I can use the test dispatcher in test.
427 Views