When I'm sending HTTP requests to server from my tablet by using Fuel, I want to display a ProgressBar at screen before the concerned function begins to run. But for an unknow reason, it's happened once the function has completed its job.
The ProgressBar is defined programmatically by this function :
protected fun initProgressBar(constraintLayoutID: Int): ProgressBar {
val originalConstraintLayout = findViewById<ConstraintLayout>(constraintLayoutID)
progressBar = ProgressBar(this).apply {
id = getInteger(R.integer.INT_PROGRESS_BAR_ID)
elevation = 999F
isIndeterminate = true
layoutParams = ViewGroup.LayoutParams(300, 300)
visibility = View.GONE
originalConstraintLayout.addView(this)
}
ConstraintSet().run {
clone(originalConstraintLayout)
for(position in listOf(ConstraintSet.TOP, ConstraintSet.START, ConstraintSet.END, ConstraintSet.BOTTOM))
connect(progressBar!!.id, position, constraintLayoutID, position,0)
applyTo(originalConstraintLayout)
}
return progressBar!!
}
I've tried to show/hide the ProgressBar lonely and it's working perfectly. Here is the inline function where it should be showed before sending the main request :
inline fun <reified T : DefaultResponse> sendRequest(
routeName: String = "",
routeArgs: String = ""
): T = runBlocking { // Help : https://fuel.gitbook.io/documentation/support/fuel-coroutines
owner.runOnUiThread {
owner.showProgressBar() // Should display at this point, not after
}
val adaptedResponse = ResponseFactory.getInstance(owner).getResponseObjectFor(T::class.java.name, routeName)
val (request, response, result) = Fuel.get(
"%s%s?%s%s".format(
networkManager.getURLConnection(),
(adaptedResponse as DefaultResponse).getRouteName(),
routeArgs,
getIndustrialTrypticArgs()
)
).awaitObjectResponseResult(adaptedResponse)
val exception =
FailedRequestException(owner.getString(R.string.STR_EXCEPTION_RM_FAILED_REQUEST)).apply {
setHTTPMessage(response.responseMessage)
setStatusCode(response.statusCode)
setUsedRequest(request.url)
}
return@runBlocking result.fold<T>(
{ responseObject ->
if (response.statusCode != 200)
throw exception
return@fold responseObject as T
},
{ error ->
throw exception.setOriginalError(error.exception)
}
)
}
Also, I've added a call to runOnUiThread for the display, with an encapsulation in a different coroutine thread with launch at some point, but it didn't work (still appearing after the execution's end of the inline function).
It's because it's an inline function ? Or there's a concern about concurrent threads ?
UPDATE N°1
Following Tenfour04's advices, I changed the sendRequest into a suspend function :
suspend inline fun <reified T : DefaultResponse> sendRequest(
routeName: String = "",
routeArgs: String = "",
isBlocking: Boolean = true
): T? {
val adaptedResponse = ResponseFactory.getInstance(owner).getResponseObjectFor(T::class.java.name, routeName) // 1
val fuelRequest = Fuel.get( // 2
"%s%s?%s%s".format(
networkManager.getURLConnection(),
(adaptedResponse as DefaultResponse).getRouteName(),
routeArgs,
getIndustrialTrypticArgs()
)
)
var fuelResponse: T? = null // 3
CoroutineScope(Dispatchers.Main).launch {
owner.showProgressBar() // 4
}
try {
fuelResponse =
if(isBlocking) {
val answer = fuelRequest.awaitObjectResponseResult(adaptedResponse) // 5
treatsResponse(answer) as T // 6
}
else {
fuelRequest.responseObject(adaptedResponse) { httpRequest, httpResponse, httpResult ->
try {
treatsResponse(httpRequest, httpResponse, httpResult) as T
}
catch (e: FailedRequestException) {
println(e.getOriginalError())
println(e.getUsedRequest())
}
finally {
owner.hideProgressBar()
}
}
null
}
}
catch (e: FailedRequestException) {
println(e.getOriginalError())
println(e.getUsedRequest())
}
finally {
owner.hideProgressBar()
}
return fuelResponse
}
The call of sendRequest is encapsulated into a CoroutineScope(Dispatchers.Main).launch. It's an improvement since the ProgressBar begins to appear within the function and not after its call, but I still want that it display before the fuelRequest.awaitObjectResponseResult(adaptedResponse) line (// 5).
awaitObjectResponseResult is also a suspend function.
UPDATE N°2
While a line-per-line debug session, I remarked the following on sendRequest : when owner.showProgressBar() is encapsulated into CoroutineScope(Dispatchers.Main).launch (// 4), it's clearly executed after // 5th instruction.
So, the actual order of execution since the beginning is always 1 -> 2 -> 3 -> 5 -> 4 -> 6.
When I add Dispatchers.Main as the second argument of awaitObjectResponseResult, the order become 1 -> 2 -> 3 -> 5 -> 6 -> 4. It looks like this function always get the priority for some reason...
runOnUiThreadis asynchronous. Since you want the behavior to be synchronous, meaning in the order the code appears, you should usewithContext(Dispatchers.Main)instead.withContextis a suspend function that can change which thread the inner code is run on synchronously.