Within an Android app, I'm trying to use Fuel to make an HTTP request within a Kotlin coroutine. My first try is to use the synchronous mode inside a wrapper like this:
launch(UI) {
val token = getToken()
println(token)
}
suspend fun getToken(): String? {
var (request, response, result = TOKEN_URL.httpGet().responseString()
return result.get()
}
But that is returning an android.os.NetworkOnMainThreadException. The Fuel documentation mentions .await() and .awaitString() extensions but I haven't figured it out.
What is the best way to make a Fuel http request within a Kotlin coroutine from the main UI thread in an Android application? Stuck on this - many thanks...
Calling blocking code from a suspend fun doesn't automagically turn it into suspending code. The function you call must already be a suspend fun itself. But, as you already noted, Fuel has first-class support for Kotlin coroutines so you don't have to write it yourself.
I've studied Fuel's test code:
Fuel.get("/uuid").awaitStringResponse().third
.fold({ data ->
assertTrue(data.isNotEmpty())
assertTrue(data.contains("uuid"))
}, { error ->
fail("This test should pass but got an error: ${error.message}")
})
This should be enough to get you going. For example, you might write a simple function as follows:
suspend fun getToken() = TOKEN_URL.httpGet().awaitStringResponse().third
From the documentation "to start a coroutine, there must be at least one suspending function, and it is usually a suspending lambda"
Try this:
async {
val token = getToken()
println(token)
}
Related
I want to wait for result, but the problem is I want to do this operation in background thread. I researched about this and got to know about async(), but to use async, my actual function should be also suspend, that is not possible, because that actual function is calling from overridden library so I can't make it suspendable!
Code:
override fun authenticate(route: Route?, response: Response): Request? {
return when (tokenProvider.isUserLoggedIn() && countOfResponse(response) <= AppConstants.INetworkValues.RETRY_API_LIMIT) {
true -> {
authenticateByRefreshToken(response).also {
LogUtils.d("API Request: $it")}}
else -> return null
}
}
#Synchronized
private fun authenticateByRefreshToken(response: Response): Request? {
.....
//TODO: Here I want to remove runblocking and want to use async
val newRefreshTokenResponse: Resource<RefreshTokenResponse?> = runBlocking { refreshTokenImpl.refreshToken() }
.....
}
Please help.
A function with this signature:
override fun authenticate(route: Route?, response: Response): Request? {
requires a synchronous result. That means a coroutine will not help you at this task at all. Coroutines are launched asynchronously (but the code inside the coroutine is synchronous). Asynchronous means it will not return a result directly. A launched coroutine returns a Job or Deferred, which you can wait for only inside other coroutines.
You could wrap your coroutine code in runBlocking instead of using launch, but then of course it will block the thread it is called from, so you have not accomplished anything by using a coroutine. runBlocking only makes sense if you have suspend functions that you need to call synchronously without a coroutine, or that you need to run in parallel while still blocking the calling thread.
I don't use Retrofit/OkHttp much, so I'm not exactly sure about this, but from briefly looking at the documentation, this appears to be an interface that you hand over to OkHttp to use. In that case, there is no reason whatsoever for you to attempt to convert it into asynchronous code. OkHttp is the one calling the function, not your own code, so you cannot control what thread it will be called from. That will be up to OkHttp, and presumably, it will sensibly run the code in an appropriate thread (maybe its asynchronous behavior can be configured elsewhere in your OkHttp setup).
Then you can call this function under the scope of Coroutine like
CoroutineScope(Dispactcher.IO).launch{
authenticateByRefreshToken()
// now you can use suspend keyword with further functions
}
I'm trying to rewrite interactors with rxjava chains to kotlin flow. In LocationHandlerImpl I'm using LocationService for getting my current location. In addOnSuccessListener and addOnFailureListener I'm emitting my model but having error:
"Suspension function can be called only within coroutine body". Am i doing it wrong? But i can call emit outside of listeners (look below flow builder)
It seems that you are trying to get the last location from the Android location service. This is one of many Task-returning calls in the Google Play Services. Kotlin already has a module, kotlinx-coroutines-play-services, that contributes a function
suspend fun <T> Task<T>.await(): T?
With that in your project, you can simply write this:
suspend fun getMyLocation(): Location? =
LocationServices.getFusedLocationProvider(context)
.lastLocation
.await()
If you want to integrate it with other Flow-based code, add this wrapper function:
fun <T> Task<T>.asFlow() = flow { emit(await()) }
and now you can write
fun getLocationAsFlow(): Flow<Location?> =
LocationServices.getFusedLocationProvider(context)
.lastLocation
.asFlow()
If, for educational purposes, you would like to see how it can be implemented directly, without the additional module, then the most straightforward approach would be as follows:
fun getLocationAsFlow() = flow {
val location = suspendCancellableCoroutine<Location?> { cont ->
LocationServices.getFusedLocationProvider(context)
.lastLocation
.addOnCompleteListener {
val e = exception
when {
e != null -> cont.resumeWithException(e)
isCanceled -> cont.cancel()
else -> cont.resume(result)
}
}
}
emit(location)
}
This is the result of inlining a simplified implementation of Task.await() into its use site.
As mentioned from Prokash (here), Flows are designed to be self contained. Your location services listener is not within the scope of the Flow.
You can however check out the callbackFlow which provides you the mechanics you are looking for to build a flow using a Callback-based API.
Callback Flow Documentation, be aware that the callback flow is still in Experimental phase.
I want to bind a button click to a suspended function inside viewmodel.
this is my code:
RegistrationActivityViewModel.kt
suspend fun register() {
if (validateFields()) {
val result = SourceplusplusApiService.invoke().registerUser(username.value!!, email.value!!, password.value!!).await()
isRegistrationCompleted.value = getResultValue(result)
}
}
activity_registration.xml
<Button
android:text="Register"
android:onClick="#{()->viewmodel.register()}"
android:textSize="16sp" />
i get a databinding error that says ActivityRegistrationBindingImpl not generated. after searching a lot i realized that when i remove the suspend keyword and comment the code inside, it works fine but it has to be a suspended function.
Does anyone know how to fix it?
You cannot data bind to a suspend function, and IMHO a viewmodel should not be exposing a suspend function in the first place. I recommend:
Step #1: Remove the suspend keyword from register()
Step #2: Rewrite register() to run your code in a suitable coroutine scope, so any suspend functions that it calls are handled properly:
fun register() {
viewModelScope.launch(Dispatchers.Main) {
if (validateFields()) {
val result = SourceplusplusApiService.invoke().registerUser(username.value!!, email.value!!, password.value!!).await()
isRegistrationCompleted.value = getResultValue(result)
}
}
}
Here, I am using the viewModelScope option provided by androidx.lifecycle:lifecycle-viewmodel-ktx version 2.1.0-alpha01 and newer. Alternatively, manage your own coroutine scope. Dispatchers.Main will ensure that any results of that work is available to you on the Android main application thread.
Now, your data binding expression can refer to register(), while you still have a coroutine scope for calling downstream suspend functions.
I am new to Kotlin, coming from C# it I am quite used to async\await
How do I wait for tvClient to get the response before returning the list of channels?
override fun getChannels(): MutableList<Channel> {
disposable = tvClient.getChannels()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{Log.d("***", it.toString())},
{Log.d("***",it.toString())}
)
TODO("wait for tvClient to return results")
return mChannels;
}
I tried using coroutines but no luck
What is the best way to wait for async operation to complete in Kotlin?
You're using RxJava and thus you should implement it in a reactive way.
If you're app is not build for it yet, you can get the value blocking. Assuming getChannels() returns a single you could just call blockingGet() instead of subscribe().
But be aware that this blocks the thread the outer getChannels() is called from.
Using coroutines might be better for you. It's a little nearer to what you know from C# and with the retrofit2-kotlin-coroutines-adapter you can integrate directly with Retrofit.
You could look into using the retrofit coroutine adapters from Jake Wharton https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter
you can check a functional implementation of kotlin v1.3 retrofit + stable coroutines using DSL here https://github.com/eriknyk/networkcall-sample/commits/master
DSL template:
fun <RESPONSE: DataResponse<*>, DATA: Any> networkCall(block: CallHandler<RESPONSE, DATA>.() -> Unit): MutableLiveData<Resource<DATA>>
= CallHandler<RESPONSE, DATA>().apply(block).makeCall()
interface DataResponse<T> {
fun retrieveData(): T
}
and using it:
fun getRepos(query: String) = networkCall<ResposResponse, List<Repo>> {
client = githubService.getRepos(query)
}
Hope it helps.
I am learning Kotlin coroutines. I've read that runBlocking is the way to bridge synchronous and asynchronous code. But what is the performance gain if the runBlocking stops the UI thread?
For example, I need to query a database in Android:
val result: Int
get() = runBlocking { queryDatabase().await() }
private fun queryDatabase(): Deferred<Int> {
return async {
var cursor: Cursor? = null
var queryResult: Int = 0
val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
try {
cursor = getHelper().readableDatabase.query(sqlQuery)
cursor?.moveToFirst()
queryResult = cursor?.getInt(0) ?: 0
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage)
} finally {
cursor?.close()
}
return#async queryResult
}
}
Querying the database would stop the main thread, so it seems that it would take the same amount of time as synchronous code? Please correct me if I am missing something.
runBlocking is the way to bridge synchronous and asynchronous code
I keep bumping into this phrase and it's very misleading.
runBlocking is almost never a tool you use in production. It undoes the asynchronous, non-blocking nature of coroutines. You can use it if you happen to already have some coroutine-based code that you want to use in a context where coroutines provide no value: in blocking calls. One typical use is JUnit testing, where the test method must just sit and wait for the coroutine to complete.
You can also use it while playing around with coroutines, inside your main method.
The misuse of runBlocking has become so widespread that the Kotlin team actually tried to add a fail-fast check which would immediately crash your code if you call it on the UI thread. By the time they did this, it was already breaking so much code that they had to remove it.
Actually you use runBlocking to call suspending functions in "blocking" code that otherwise wouldn't be callable there or in other words: you use it to call suspend functions outside of the coroutine context (in your example the block passed to async is the suspend function). Also (more obvious, as the name itself implies already), the call then is a blocking call. So in your example it is executed as if there wasn't something like async in place. It waits (blocks interruptibly) until everything within the runBlocking-block is finished.
For example assume a function in your library as follows:
suspend fun demo() : Any = TODO()
This method would not be callable from, e.g. main. For such a case you use runBlocking then, e.g.:
fun main(args: Array<String>) {
// demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
// whereas the following works as intended:
runBlocking {
demo()
} // it also waits until demo()-call is finished which wouldn't happen if you use launch
}
Regarding performance gain: actually your application may rather be more responsive instead of being more performant (sometimes also more performant, e.g. if you have multiple parallel actions instead of several sequential ones). In your example however you already block when you assign the variable, so I would say that your app doesn't get more responsive yet. You may rather want to call your query asynchronously and then update the UI as soon as the response is available. So you basically just omit runBlocking and rather use something like launch. You may also be interested in Guide to UI programming with coroutines.