https://kotlinlang.org logo
Title
s

Schadenfreude

05/02/2019, 2:54 PM
Hey guys, I’m trying to await a result of a callback from the Amazon LWA sdk but it’s a bit advanced and confusing for me. This is their method:
AuthorizationManager.getToken(context, SCOPES, tokenListener)
I want to wait for it to get the result in the listener and then continue on with my code but I can’t figure out if it’s at all possible.
s

streetsofboston

05/02/2019, 2:59 PM
suspend fun getAuthManagerToken(context: Context, scopes: Scopes) : Token = suspendCoroutine { cont ->
    AuthorizationManager.getToken(context, scopes) { token ->
        cont.resume(token)
    }
}
s

Schadenfreude

05/02/2019, 3:03 PM
The listener can return either a token or an error, its type is
Listener<AuthorizeResult, AuthError>
s

streetsofboston

05/02/2019, 3:05 PM
Is
AuthError
a
Throwable
?
l

louiscad

05/02/2019, 3:05 PM
@Schadenfreude Then use
resumeWithException
when there's an error, and if there's no
Throwable
, use your own
s

Schadenfreude

05/02/2019, 3:06 PM
@streetsofboston, yes it’s a throwable
s

streetsofboston

05/02/2019, 3:06 PM
suspend fun getAuthManagerToken(context: Context, scopes: Scopes) : Token = suspendCoroutine { cont ->
    AuthorizationManager.getToken(context, scopes) { token, error ->
        if (error != null) resumeWithException(error)
        else cont.resume(token)
    }
}
(I assumed the type(-name)s in my example… I don’t know their exact form)
s

Schadenfreude

05/02/2019, 3:33 PM
So thanks to @streetsofboston I managed to figure it out here’s the final solution:
private suspend fun getAuthManagerToken(context: Context, scopes: Array<Scope>): AuthorizeResult =
        suspendCoroutine { cont ->
            AuthorizationManager.getToken(context, scopes, object : Listener<AuthorizeResult, AuthError> {
                override fun onError(authError: AuthError) {
                    cont.resumeWithException(authError)
                }

                override fun onSuccess(authResult: AuthorizeResult) {
                    cont.resume(authResult)
                }
            })
        }
s

Sam

05/02/2019, 8:05 PM
@Schadenfreude @streetsofboston Is there any reason not to use suspendCancellableCoroutine and cancel getToken() when not needed anymore?
s

streetsofboston

05/02/2019, 8:10 PM
`suspendCancellableCoroutine`: In the code-sample of Daniel, the
getAuthManagerToken
has no reason to cancel the suspended coroutine itself. The outer-scope that calls
getAuthManagerToken
still can cancel the suspended fun. (
suspendCancellableCoroutine
will just provide a
cont
to the implementation of
getAuthManagerToken
that now has an additional
cancel
function.)
s

Sam

05/02/2019, 8:13 PM
Okay, if getToken() returns a cancellable call, then it makes sense to use suspendCancellableCoroutine
that way the pending getToken() request and its resources can be dropped early
s

streetsofboston

05/02/2019, 8:21 PM
If a cancelation happens, most of the time the outer-scope that calls the function does the cancelation. The
getAuthManager
doesn’t need to worry about it in this case. If the
getAuthManagerToken
itself needs to cancel it, then it needs to call
suspendCancellableCoroutine
so that it has a
CancelableContinuation
(not sure if I have the class-name right) instead of a plain
Continuation
and now it can call
cancel()
on it, if necessary, instead of calling
resumeWithException(...)
. Cancelation is almost(!!) like
resumeWithException
, because it cancels the suspending call, but it is not ‘fatal’, it has just canceled.
p

pakoito

05/02/2019, 11:38 PM
@louiscad I thought resumeWithExceptions was only for crashing cases, not for control flow
l

louiscad

05/02/2019, 11:47 PM
@pakoito It's to forward `Throwable`s back into the control flow from error/exceptions callbacks