Add implementation of blocking requests

This commit is contained in:
Kirill Nayduik 2022-04-12 15:56:24 +03:00
parent 3d68ef1c51
commit 5ff0c5889f
5 changed files with 127 additions and 0 deletions

View File

@ -0,0 +1,54 @@
package ru.touchin.network.blocking
import okhttp3.Request
import okio.Timeout
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import ru.touchin.network.utils.getAnnotation
class BlockingCall(
private val callDelegate: Call<Any>
) : Call<Any> {
override fun clone(): Call<Any> = callDelegate.clone()
override fun execute(): Response<Any> {
return callDelegate.execute()
}
override fun enqueue(callback: Callback<Any>) {
if (PendingRequestsManager.isPending.get()) {
PendingRequestsManager.addPendingRequest(callDelegate, callback)
return
}
val isBlocking = callDelegate.isBlocking()
if (isBlocking) PendingRequestsManager.isPending.set(true)
callDelegate.enqueue(object: Callback<Any> {
override fun onResponse(call: Call<Any>, response: Response<Any>) {
callback.onResponse(call, response)
if (isBlocking) PendingRequestsManager.executePendingRequests()
}
override fun onFailure(call: Call<Any>, t: Throwable) {
callback.onFailure(call, t)
if (isBlocking) PendingRequestsManager.dropPendingRequests()
}
})
}
override fun isExecuted(): Boolean = callDelegate.isExecuted
override fun cancel() = callDelegate.cancel()
override fun isCanceled(): Boolean = callDelegate.isCanceled
override fun request(): Request = callDelegate.request()
override fun timeout(): Timeout = callDelegate.timeout()
private fun Call<Any>.isBlocking() = request().getAnnotation(BlockingRequest::class.java) != null
}

View File

@ -0,0 +1,5 @@
package ru.touchin.network.blocking
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class BlockingRequest(val abortOnFail: Boolean = false)

View File

@ -0,0 +1,27 @@
package ru.touchin.network.blocking
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.Retrofit
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
class BlockingRequestCallAdapter private constructor(
private val responseType: Type
) : CallAdapter<Any, Any> {
companion object {
fun create() = object : CallAdapter.Factory() {
override fun get(returnType: Type, annotations: Array<out Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
return (returnType as? ParameterizedType)
?.let { BlockingRequestCallAdapter(responseType = it.actualTypeArguments[0]) }
}
}
}
override fun responseType(): Type = responseType
override fun adapt(call: Call<Any>): Any {
return BlockingCall(call)
}
}

View File

@ -0,0 +1,35 @@
package ru.touchin.network.blocking
import retrofit2.Call
import retrofit2.Callback
import java.util.concurrent.atomic.AtomicBoolean
object PendingRequestsManager {
val isPending = AtomicBoolean(false)
private val pendingRequests = mutableListOf<Pair<Call<Any>, Callback<Any>>>()
fun addPendingRequest(call: Call<Any>, callback: Callback<Any>) {
pendingRequests.add(call to callback)
}
@Synchronized
fun executePendingRequests() {
applyActionToPendingRequests { first.enqueue(second) }
}
@Synchronized
fun dropPendingRequests() {
applyActionToPendingRequests { first.cancel() }
}
private fun applyActionToPendingRequests(action: Pair<Call<Any>, Callback<Any>>.() -> Unit) {
isPending.set(false)
pendingRequests.forEach { it.action() }
pendingRequests.clear()
}
}

View File

@ -0,0 +1,6 @@
package ru.touchin.network.utils
import okhttp3.Request
import retrofit2.Invocation
fun <T: Annotation> Request.getAnnotation(annotation: Class<T>) = tag(Invocation::class.java)?.method()?.getAnnotation(annotation)