call a suspend function inside a normal function - android

I want to call blocking a suspend function in a normal function, but does not block Thread to finishing suspend function and then return Response
override fun intercept(chain: Interceptor.Chain): Response {
// getSession is a suspend function
val session = sessionProvider.getSession()
return chain.proceed(
chain
.request()
.newBuilder()
.addHeader("Authorization", "${session.token}")
.build()
)
}

This looks like you are implementing an OkHttp interceptor, so I am hoping that intercept() is being called on a background thread.
If so, use runBlocking():
override fun intercept(chain: Interceptor.Chain): Response {
// getSession is a suspend function
val session = runBlocking { sessionProvider.getSession() }
return chain.proceed(
chain
.request()
.newBuilder()
.addHeader("Authorization", "${session.token}")
.build()
)
}
runBlocking() will execute the suspend function, blocking the current thread until that work is complete.

Related

Suspend Coroutines's execution to wait for callback

Recently, I applied Coroutines into my project everything seems to be fine but today I meet a problem [Upload file/image into server using Coroutine + Retrofit][1]
It seems that there is no solution for upload file using coroutine + Retrofit, so we must use callback for retrofit.
//Api interface
interface UploadFileApiKotlin {
#Multipart
#POST("/uploadFileApi")
fun uploadFiles(
#Part listUri: List<MultipartBody.Part>
): Call<Response<List<FileResponse>?>>
}
//ViewModel class
serviceScope.launch {
//Insert into db
repository.insertNewExtAct()
//Send data into server.
val call = RequestHelper.getUpLoadFilesKotlinRequest().uploadFiles(partList)
call.enqueue(object : Callback<Response<List<FileResponse>?>> {
override fun onResponse(
call: Call<Response<List<FileResponse>?>>,
response: Response<Response<List<FileResponse>?>>
) {
TODO("Not yet implemented")
}
override fun onFailure(
call: Call<Response<List<FileResponse>?>>,
t: Throwable
) {
TODO("Not yet implemented")
}
})
//Wait for response from server and do logic
}
My question is: How can I suspend coroutines's execution to wait for retrofit's callback ?
Thank you bro.
[1]: Upload file/image into server using Coroutine + Retrofit
You can use suspendCoroutine or suspendCancellableCoroutine to work with callbacks (to convert a callback to a suspend function):
serviceScope.launch {
//Insert into db
repository.insertNewExtAct()
//Send data into server.
uploadFile()
//Wait for response from server and do logic
}
suspend fun uploadFile() = suspendCoroutine<Response<List<FileResponse>?>> { continuation ->
val call = RequestHelper.getUpLoadFilesKotlinRequest().uploadFiles(partList)
call.enqueue(object : Callback<Response<List<FileResponse>?>> {
override fun onResponse(
call: Call<Response<List<FileResponse>?>>,
response: Response<Response<List<FileResponse>?>>
) {
// Resume coroutine with a value provided by the callback
continuation.resumeWith(response.data)
}
override fun onFailure(
call: Call<Response<List<FileResponse>?>>,
t: Throwable
) {
continuation.resumeWithException(t)
}
})
}
suspendCoroutine suspends coroutine in which it executed until we decide to continue by calling appropriate methods - Continuation.resume.... suspendCoroutine mainly used when we have some legacy code with callbacks.
There is also suspendCancellableCoroutine builder function, it behaves similar to suspendCoroutine with additional feature - provides an implementation of CancellableContinuation to the block.

how to use Authenticator with suspend api function? Retrofit

I am building client:
OkHttpClient().newBuilder()
.authenticator(object : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
val request = AppPreferences.refreshToken?.let {
api.refreshTokenC(it).execute()
}
...
})
and for this I use usual fun which returns Call :
#POST("Accounts/refresh")
fun refreshTokenC(#Query("refreshToken") refreshToken: String): Call<TokenResponse>
But how can use it with suspend function?:
#POST("Accounts/refresh")
suspend fun refreshToken(#Query("refreshToken") refreshToken: String): Response<TokenResponse>
fun authenticate(route: Route?, response: Response) is not suspend function, we cannot invoke suspend function from it.
Can use runBlocking:
Runs a new coroutine and blocks the current thread interruptibly until its completion. This function should not be used from a coroutine. It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests.
fun authenticate(route: Route?, response: Response) is being called on a background thread, we can use runBlocking safely.
val request = AppPreferences.refreshToken?.let { token ->
runBlocking { api.refreshToken(token) }
}
Similar code: AuthInterceptor.kt

Is it better to call .enqueue in a normal function or .execute in a kotlin suspend function?

Here is what I already know:
Retrofit has the enqueue function and the execute function. The enqueue function executes on a different (background) thread and then returns the response using the callback. The execute function executes on the calling Thread and returns the response directly. enqueue can be called on the UI Thread while execute shouldn't be called on the UI Thread.
But I am now wondering, which of the following two options is better.
Call enqueue in a normal function:
fun makeRequest() {
getCall().enqueue(
object : Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
if (response.isSuccessful) {
//unsuccessful
}
//successful
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
//failed
}
}
)
}
or call execute in a suspend function on a background thread:
suspend fun makeRequest() = withContext(Dispatchers.IO) {
val call = getCall()
try {
val response = call.execute()
if (!response.isSuccessful) {
//unsuccessful
}
//successful
} catch (t: Throwable) {
//failed
}
}
Which one of these is preferable?
Coroutines have cleaner syntax, so that's a plus. And if you're familiar with coroutine SupervisorJob, you can more easily cancel groups of requests. Other than that, they are largely the same except for which background thread is getting used for the request. But Retrofit already has built-in coroutine support, so your second version can be cleaner than what you have:
suspend fun makeRequest() { // Can be called from any dispatcher
try {
val response = getCall().awaitResponse()
if (!response.isSuccessful) {
//unsuccessful
}
//successful
} catch (t: Throwable) {
//failed
}
}

Make an async API request blocking with Kotlin coroutines?

As the title implies, I'm currently working with an API that has an async callback method. I'd like to be able to WAIT for the result before proceeding with the rest of the code (making an existing async call somewhat synchronous, if that makes sense). Is this possible with coroutines? This is what my code looks like right now, but the request is still called multiple times (I'm new to coroutines).
class TokenAuthenticator #Inject constructor(val prefs: AppPrefs) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (response.code() == ApiErrorCode.UNAUTHORIZED) {
Timber.w("Unauthorized. Refreshing token...")
val token: String? = runBlocking(Dispatchers.Main) { refreshToken() }
token?.let {
return response
.request()
.newBuilder()
.header("Authorization", "Bearer $token")
.build()
}
}
when (response.code()) {
ApiErrorCode.UNAUTHORIZED -> Timber.w("Tried to refresh token. Failed?")
else -> Timber.d("NOT refreshing token, response code was: ${response.code()}")
}
return response.request()
}
private suspend fun refreshToken() = suspendCoroutine<String?> {
SomeApi.getValidAccessToken(object : TokenCallback {
override fun onSuccess(accessToken: String?) {
Timber.d("Token successfully retrieved. Storing to prefs...")
prefs.userAuthToken = accessToken
it.resume(accessToken)
}
override fun onError(errorData: Any?) {
Timber.e("Error retrieving token")
it.resume(null)
}
})
}
}

Waiting for thread to complete OkHttp Call

I have been having problems with OkHttp when I nest an OkHttp call inside another OkHttp call I am having a problem with the concurrency. I want to wait for my inner call to finish its thread's job before proceeding. Please take a look.
Note: I am a novice with Kotlin and Multi-thread handling.
private fun parseJson(url: String) {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response?) {
var bodyOfProducts = response?.body()?.string()
var collectionJsonObject = jsonParseTool.fromJson(bodyOfProducts, Products::class.java)
val productsWithDetails = ArrayList<ProductDetails>()
for(product in collectionJsonObject.collects){
var concatProductUrl = "https://shopicruit.myshopify.com/admin/products.json?ids=" + product.product_id+ "&page=1&access_token=c32313df0d0ef512ca64d5b336a0d7c6"
val newRequest = Request.Builder()
.url(concatProductUrl)
.build()
val job = thread {
client.newCall(newRequest).enqueue(object : Callback {
override fun onResponse(call: Call, newResponse: Response?) {
var bodyOfProductDetails = newResponse?.body()?.string()
var productJsonObject = jsonParseTool.fromJson(bodyOfProductDetails, ProductDetails::class.java)
productsWithDetails.add(productJsonObject)
}
override fun onFailure(call: Call, e: IOException) {
println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
}
})
}
job.start()
job.join() // This should force my thread to finish before the rest of the code is executed on the main thread.
}
// println(collectionJsonObject.collects[0].product_id)
/*runOnUiThread {
recyclerViewCustomCollections.adapter = CollectionsAdapter(jsonObject)
}*/
}
override fun onFailure(call: Call, e: IOException) {
println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
}
})
}
In this case you should be using execute as mentioned and since http calls are handled asynchronously your thread is redundant and should be removed.
If you want to run code after all the requests are finished one way of doing this is by passing in a onComplete callback function and count the number of requests completed, when all of the threads are completed call the callback function containing the code that should be run after all of the requests.

Categories

Resources